mirror of
https://github.com/PixelGuys/Cubyz.git
synced 2025-08-04 03:37:59 -04:00
Start working on the client-side entity code.
This commit is contained in:
parent
6a5ea7f1d9
commit
d0d4b624c1
136
src/entity.zig
Normal file
136
src/entity.zig
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
const JsonElement = @import("json.zig").JsonElement;
|
||||||
|
const renderer = @import("renderer.zig");
|
||||||
|
const settings = @import("settings.zig");
|
||||||
|
const utils = @import("utils.zig");
|
||||||
|
const vec = @import("vec.zig");
|
||||||
|
const Vec3d = vec.Vec3d;
|
||||||
|
const Vec3f = vec.Vec3f;
|
||||||
|
|
||||||
|
pub const ClientEntity = struct {
|
||||||
|
interpolatedValues: utils.GenericInterpolation(6) = undefined,
|
||||||
|
|
||||||
|
width: f64,
|
||||||
|
height: f64,
|
||||||
|
// TODO:
|
||||||
|
// public final EntityType type;
|
||||||
|
|
||||||
|
pos: Vec3d = undefined,
|
||||||
|
rot: Vec3f = undefined,
|
||||||
|
|
||||||
|
id: u32,
|
||||||
|
name: []const u8,
|
||||||
|
|
||||||
|
pub fn init(self: *ClientEntity) void {
|
||||||
|
self.interpolatedValues.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getRenderPosition(self: *ClientEntity) Vec3d {
|
||||||
|
return Vec3d{.x = self.pos.x, .y = self.pos.y + self.height/2, .z = self.pos.z};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn updatePosition(self: *ClientEntity, pos: [3]f64, vel: [3]f64, time: i16) void {
|
||||||
|
self.interpolatedValues.updatePosition(pos, vel, time);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update(self: *ClientEntity, time: i16, lastTime: i16) void {
|
||||||
|
self.interpolatedValues.update(time, lastTime);
|
||||||
|
self.pos.x = self.interpolatedValues.outPos[0];
|
||||||
|
self.pos.y = self.interpolatedValues.outPos[1];
|
||||||
|
self.pos.z = self.interpolatedValues.outPos[2];
|
||||||
|
self.rot.x = @floatCast(f32, self.interpolatedValues.outPos[3]);
|
||||||
|
self.rot.y = @floatCast(f32, self.interpolatedValues.outPos[4]);
|
||||||
|
self.rot.z = @floatCast(f32, self.interpolatedValues.outPos[5]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const ClientEntityManager = struct {
|
||||||
|
var lastTime: i16 = 0;
|
||||||
|
var timeDifference: utils.TimeDifference = utils.TimeDifference{};
|
||||||
|
pub var entities: std.ArrayList(ClientEntity) = undefined;
|
||||||
|
pub var mutex: std.Thread.Mutex = std.Thread.Mutex{};
|
||||||
|
|
||||||
|
pub fn init() void {
|
||||||
|
entities = std.ArrayList(ClientEntity).init(renderer.RenderStructure.allocator); // TODO: Use world allocator.
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deinit() void {
|
||||||
|
entities.deinit();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clear() void {
|
||||||
|
entities.clearRetainingCapacity();
|
||||||
|
timeDifference = utils.TimeDifference{};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update() void {
|
||||||
|
mutex.lock();
|
||||||
|
defer mutex.unlock();
|
||||||
|
var time = @intCast(i16, std.time.milliTimestamp() & 65535);
|
||||||
|
time -%= timeDifference.difference;
|
||||||
|
for(entities.items) |*ent| {
|
||||||
|
ent.update(time, lastTime);
|
||||||
|
}
|
||||||
|
lastTime = time;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn addEntity(json: JsonElement) !void {
|
||||||
|
mutex.lock();
|
||||||
|
defer mutex.unlock();
|
||||||
|
var ent = try entities.addOne();
|
||||||
|
ent.* = ClientEntity{
|
||||||
|
.id = json.get(u32, "id", std.math.maxInt(u32)),
|
||||||
|
// TODO:
|
||||||
|
// CubyzRegistries.ENTITY_REGISTRY.getByID(json.getString("type", null)),
|
||||||
|
.width = json.get(f64, "width", 1),
|
||||||
|
.height = json.get(f64, "height", 1),
|
||||||
|
.name = json.get([]const u8, "name", 1),
|
||||||
|
};
|
||||||
|
ent.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn removeEntity(id: u32) void {
|
||||||
|
mutex.lock();
|
||||||
|
defer mutex.unlock();
|
||||||
|
for(entities.items) |*ent, i| {
|
||||||
|
if(ent.id == id) {
|
||||||
|
entities.swapRemove(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn serverUpdate(time: i16, data: []const u8) !void {
|
||||||
|
mutex.lock();
|
||||||
|
defer mutex.unlock();
|
||||||
|
timeDifference.addDataPoint(time);
|
||||||
|
std.debug.assert(data.len%(4 + 24 + 12 + 24) == 0);
|
||||||
|
var remaining = data;
|
||||||
|
while(remaining.len != 0) {
|
||||||
|
const id = std.mem.readIntBig(u32, remaining[0..4]);
|
||||||
|
remaining = remaining[4..];
|
||||||
|
const pos = [_]f64 {
|
||||||
|
@bitCast(f64, std.mem.readIntBig(u64, remaining[0..8])),
|
||||||
|
@bitCast(f64, std.mem.readIntBig(u64, remaining[8..16])),
|
||||||
|
@bitCast(f64, std.mem.readIntBig(u64, remaining[16..24])),
|
||||||
|
@floatCast(f64, @bitCast(f32, std.mem.readIntBig(u32, remaining[24..28]))),
|
||||||
|
@floatCast(f64, @bitCast(f32, std.mem.readIntBig(u32, remaining[28..32]))),
|
||||||
|
@floatCast(f64, @bitCast(f32, std.mem.readIntBig(u32, remaining[32..36]))),
|
||||||
|
};
|
||||||
|
remaining = remaining[36..];
|
||||||
|
const vel = [_]f64 {
|
||||||
|
@bitCast(f64, std.mem.readIntBig(u64, remaining[0..8])),
|
||||||
|
@bitCast(f64, std.mem.readIntBig(u64, remaining[8..16])),
|
||||||
|
@bitCast(f64, std.mem.readIntBig(u64, remaining[16..24])),
|
||||||
|
0, 0, 0,
|
||||||
|
};
|
||||||
|
remaining = remaining[24..];
|
||||||
|
for(entities.items) |*ent| {
|
||||||
|
if(ent.id == id) {
|
||||||
|
ent.updatePosition(pos, vel, time);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
@ -3,6 +3,7 @@ const std = @import("std");
|
|||||||
const assets = @import("assets.zig");
|
const assets = @import("assets.zig");
|
||||||
const blocks = @import("blocks.zig");
|
const blocks = @import("blocks.zig");
|
||||||
const chunk = @import("chunk.zig");
|
const chunk = @import("chunk.zig");
|
||||||
|
const entity = @import("entity.zig");
|
||||||
const game = @import("game.zig");
|
const game = @import("game.zig");
|
||||||
const graphics = @import("graphics.zig");
|
const graphics = @import("graphics.zig");
|
||||||
const renderer = @import("renderer.zig");
|
const renderer = @import("renderer.zig");
|
||||||
@ -245,6 +246,9 @@ pub fn main() !void {
|
|||||||
try renderer.init();
|
try renderer.init();
|
||||||
defer renderer.deinit();
|
defer renderer.deinit();
|
||||||
|
|
||||||
|
entity.ClientEntityManager.init();
|
||||||
|
defer entity.ClientEntityManager.deinit();
|
||||||
|
|
||||||
network.init();
|
network.init();
|
||||||
|
|
||||||
try renderer.RenderStructure.init();
|
try renderer.RenderStructure.init();
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
pub const defaultPort: u16 = 47649;
|
pub const defaultPort: u16 = 47649;
|
||||||
pub const connectionTimeout = 60000;
|
pub const connectionTimeout = 60000;
|
||||||
|
|
||||||
|
pub const entityLookback: i16 = 100;
|
||||||
|
|
||||||
pub const version = "0.12.0";
|
pub const version = "0.12.0";
|
||||||
|
|
||||||
pub const highestLOD: u5 = 5;
|
pub const highestLOD: u5 = 5;
|
||||||
|
169
src/utils.zig
169
src/utils.zig
@ -334,4 +334,173 @@ pub const ThreadPool = struct {
|
|||||||
defer self.loadList.mutex.unlock();
|
defer self.loadList.mutex.unlock();
|
||||||
return self.loadList.size;
|
return self.loadList.size;
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn GenericInterpolation(comptime elements: comptime_int) type {
|
||||||
|
const frames: usize = 8;
|
||||||
|
return struct {
|
||||||
|
lastPos: [frames][elements]f64,
|
||||||
|
lastVel: [frames][elements]f64,
|
||||||
|
lastTimes: [frames]i16,
|
||||||
|
frontIndex: u32,
|
||||||
|
currentPoint: i32,
|
||||||
|
outPos: [elements]f64,
|
||||||
|
outVel: [elements]f64,
|
||||||
|
|
||||||
|
pub fn initPosition(self: *@This(), initialPosition: *[elements]f64) void {
|
||||||
|
std.mem.copy(f64, &self.outPos, initialPosition);
|
||||||
|
std.mem.set([elements]f64, &self.lastPos, self.outPos);
|
||||||
|
std.mem.set(f64, &self.outVel, 0);
|
||||||
|
std.mem.set([elements]f64, &self.lastVel, self.outVel);
|
||||||
|
self.frontIndex = 0;
|
||||||
|
self.currentPoint = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init(self: *@This(), initialPosition: *[elements]f64, initialVelocity: *[elements]f64) void {
|
||||||
|
std.mem.copy(f64, &self.outPos, initialPosition);
|
||||||
|
std.mem.set([elements]f64, &self.lastPos, self.outPos);
|
||||||
|
std.mem.copy(f64, &self.outVel, initialVelocity);
|
||||||
|
std.mem.set([elements]f64, &self.lastVel, self.outVel);
|
||||||
|
self.frontIndex = 0;
|
||||||
|
self.currentPoint = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn updatePosition(self: *@This(), pos: *[elements]f64, vel: *[elements]f64, time: i16) void {
|
||||||
|
self.frontIndex = (self.frontIndex + 1)%frames;
|
||||||
|
std.mem.copy(f64, &self.lastPos[self.frontIndex], pos);
|
||||||
|
std.mem.copy(f64, &self.lastVel[self.frontIndex], vel);
|
||||||
|
self.lastTimes[self.frontIndex] = time;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn evaluateSplineAt(_t: f64, tScale: f64, p0: f64, _m0: f64, p1: f64, _m1: f64) [2]f64 {
|
||||||
|
// https://en.wikipedia.org/wiki/Cubic_Hermite_spline#Unit_interval_(0,_1)
|
||||||
|
const t = _t/tScale;
|
||||||
|
const m0 = _m0*tScale;
|
||||||
|
const m1 = _m1*tScale;
|
||||||
|
const t2 = t*t;
|
||||||
|
const t3 = t2*t;
|
||||||
|
const a0 = p0;
|
||||||
|
const a1 = m0;
|
||||||
|
const a2 = -3*p0 - 2*m0 + 3*p1 - m1;
|
||||||
|
const a3 = 2*p0 + m0 - 2*p1 + m1;
|
||||||
|
return [_]f64 {
|
||||||
|
a0 + a1*t + a2*t2 + a3*t3, // value
|
||||||
|
(a1 + 2*a2*t + 3*a3*t2)/tScale, // first derivative
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn interpolateCoordinate(self: *@This(), i: u32, t: f64, tScale: f64) void {
|
||||||
|
if(self.outVel[i] == 0 and self.lastVel[self.currentPoint][i] == 0) {
|
||||||
|
self.outPos += (self.lastPos[self.currentPoint][i] - self.outPos[i])*t/tScale;
|
||||||
|
} else {
|
||||||
|
// Use cubic interpolation to interpolate the velocity as well.
|
||||||
|
const newValue = evaluateSplineAt(t, tScale, self.outPos[i], self.outVel[i], self.lastPos[self.currentPoint][i], self.lastVel[self.currentPoint][i]);
|
||||||
|
self.outPos = newValue[0];
|
||||||
|
self.outVel = newValue[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn determineNextDataPoint(self: *@This(), time: i16, lastTime: *i16) void {
|
||||||
|
if(self.currentPoint != -1 and self.lastTimes[self.currentPoint] -% time <= 0) {
|
||||||
|
// Jump to the last used value and adjust the time to start at that point.
|
||||||
|
lastTime.* = self.lastTimes[self.currentPoint];
|
||||||
|
std.mem.copy(f64, &self.outPos, &self.lastPos[self.currentPoint]);
|
||||||
|
std.mem.copy(f64, &self.outVel, &self.lastVel[self.currentPoint]);
|
||||||
|
self.currentPoint = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(self.currentPoint == -1) {
|
||||||
|
// Need a new point:
|
||||||
|
var smallestTime: i16 = std.math.maxInt(i16);
|
||||||
|
var smallestIndex: i32 = -1;
|
||||||
|
for(self.lastTimes) |_, i| {
|
||||||
|
// ↓ Only using a future time value that is far enough away to prevent jumping.
|
||||||
|
if(self.lastTimes[i] -% time >= 50 and self.lastTimes[i] -% time < smallestTime) {
|
||||||
|
smallestTime = self.lastTimes[i] -% time;
|
||||||
|
smallestIndex = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.currentPoint = smallestIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update(self: *@This(), time: i16, _lastTime: i16) void {
|
||||||
|
var lastTime = _lastTime;
|
||||||
|
self.determineNextDataPoint(time, &lastTime);
|
||||||
|
|
||||||
|
var deltaTime = @intToFloat(f64, time -% lastTime)/1000;
|
||||||
|
if(deltaTime < 0) {
|
||||||
|
std.log.err("Experienced time travel. Current time: {} Last time: {}", .{time, lastTime});
|
||||||
|
deltaTime = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(self.currentPoint == -1) {
|
||||||
|
for(self.outPos) |*pos, i| {
|
||||||
|
// Just move on with the current velocity.
|
||||||
|
pos.* += self.outVel[i]*deltaTime;
|
||||||
|
// Add some drag to prevent moving far away on short connection loss.
|
||||||
|
self.outVel[i] *= std.math.pow(f64, 0.5, deltaTime);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const tScale = @intToFloat(f64, self.lastTimes[self.currentPoint] -% lastTime)/1000;
|
||||||
|
const t = deltaTime;
|
||||||
|
for(self.outPos) |_, i| {
|
||||||
|
self.interpolateCoordinate(i, t, tScale);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn updateIndexed(self: *@This(), time: i16, _lastTime: i16, indices: []u16, coordinatesPerIndex: comptime_int) void {
|
||||||
|
var lastTime = _lastTime;
|
||||||
|
self.determineNextDataPoint(time, &lastTime);
|
||||||
|
|
||||||
|
var deltaTime = @intToFloat(f64, time -% lastTime)/1000;
|
||||||
|
if(deltaTime < 0) {
|
||||||
|
std.log.err("Experienced time travel. Current time: {} Last time: {}", .{time, lastTime});
|
||||||
|
deltaTime = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(self.currentPoint == -1) {
|
||||||
|
for(indices) |i| {
|
||||||
|
const index = i*coordinatesPerIndex;
|
||||||
|
var j: u32 = 0;
|
||||||
|
while(j < coordinatesPerIndex): (j += 1) {
|
||||||
|
// Just move on with the current velocity.
|
||||||
|
self.outPos[index + j] += self.outVel[index + j]*deltaTime;
|
||||||
|
// Add some drag to prevent moving far away on short connection loss.
|
||||||
|
self.outVel[index + j] *= std.math.pow(f64, 0.5, deltaTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const tScale = @intToFloat(f64, self.lastTimes[self.currentPoint] -% lastTime)/1000;
|
||||||
|
const t = deltaTime;
|
||||||
|
for(indices) |i| {
|
||||||
|
const index = i*coordinatesPerIndex;
|
||||||
|
var j: u32 = 0;
|
||||||
|
while(j < coordinatesPerIndex): (j += 1) {
|
||||||
|
self.interpolateCoordinate(index + j, t, tScale);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const TimeDifference = struct {
|
||||||
|
difference: i16 = 0,
|
||||||
|
firstValue: bool = true,
|
||||||
|
|
||||||
|
pub fn addDataPoint(self: *TimeDifference, time: i16) void {
|
||||||
|
const currentTime = @intCast(i16, std.time.milliTimestamp() & 65535);
|
||||||
|
const timeDifference = currentTime -% time;
|
||||||
|
if(self.firstValue) {
|
||||||
|
self.difference = timeDifference;
|
||||||
|
self.firstValue = false;
|
||||||
|
}
|
||||||
|
if(timeDifference -% self.difference > 0) {
|
||||||
|
self.difference +%= 1;
|
||||||
|
} else if(timeDifference -% self.difference < 0) {
|
||||||
|
self.difference -%= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
Loading…
x
Reference in New Issue
Block a user