add switching to simulated mode in gui (freezes on exitting)

This commit is contained in:
Rokas Puzonas 2025-01-15 01:21:01 +02:00
parent b6859909ab
commit 1cca8c5806
8 changed files with 1120 additions and 385 deletions

View File

@ -20,6 +20,10 @@ pub fn subtract(self: Position, other: Position) Position {
return init(self.x - other.x, self.y - other.y); return init(self.x - other.x, self.y - other.y);
} }
pub fn multiply(self: Position, other: Position) Position {
return init(self.x * other.x, self.y * other.y);
}
pub fn distance(self: Position, other: Position) f32 { pub fn distance(self: Position, other: Position) f32 {
const dx: f32 = @floatFromInt(self.x - other.x); const dx: f32 = @floatFromInt(self.x - other.x);
const dy: f32 = @floatFromInt(self.y - other.y); const dy: f32 = @floatFromInt(self.y - other.y);

View File

@ -459,6 +459,14 @@ pub fn prefetch(self: *Server, allocator: std.mem.Allocator, opts: PrefetchOptio
_ = try self.getImage(.map, skin); _ = try self.getImage(.map, skin);
} }
} }
inline for (std.meta.fields(Character.Skin)) |field| {
const skin: Character.Skin = @enumFromInt(field.value);
const skin_name = skin.toString();
if (self.store.images.getId(.character, skin_name) == null) {
_ = try self.getImage(.character, skin_name);
}
}
} }
} }

View File

@ -12,6 +12,7 @@ const rlgl_h = @cImport({
}); });
const assert = std.debug.assert; const assert = std.debug.assert;
const log = std.log.scoped(.app);
const App = @This(); const App = @This();
@ -27,6 +28,7 @@ map_textures: std.ArrayList(MapTexture),
map_texture_indexes: std.ArrayList(usize), map_texture_indexes: std.ArrayList(usize),
map_position_min: Api.Position, map_position_min: Api.Position,
map_position_max: Api.Position, map_position_max: Api.Position,
character_skin_textures: std.EnumArray(Api.Character.Skin, rl.Texture2D),
camera: rl.Camera2D, camera: rl.Camera2D,
font_face: FontFace, font_face: FontFace,
@ -35,7 +37,20 @@ blur_texture_horizontal: ?rl.RenderTexture = null,
blur_texture_both: ?rl.RenderTexture = null, blur_texture_both: ?rl.RenderTexture = null,
blur_shader: rl.Shader, blur_shader: rl.Shader,
pub fn init(allocator: std.mem.Allocator, store: *Api.Store, server: *Api.Server) !App { simulation: bool = false,
system_clock: Artificer.SystemClock,
started_at: i128,
artificer: Artificer.ArtificerApi,
sim_artificer: Artificer.ArtificerSim,
sim_server: Artificer.SimServer,
sim_started_at: i128,
last_sim_timestamp: i128 = 0,
thread_running: bool = true,
artificer_thread: std.Thread,
pub fn init(allocator: std.mem.Allocator, store: *Api.Store, server: *Api.Server) !*App {
var map_textures = std.ArrayList(MapTexture).init(allocator); var map_textures = std.ArrayList(MapTexture).init(allocator);
errdefer map_textures.deinit(); errdefer map_textures.deinit();
errdefer { errdefer {
@ -54,20 +69,10 @@ pub fn init(allocator: std.mem.Allocator, store: *Api.Store, server: *Api.Server
for (map_image_ids) |image_id| { for (map_image_ids) |image_id| {
const image = store.images.get(image_id).?; const image = store.images.get(image_id).?;
const texture = rl.loadTextureFromImage(rl.Image{
.width = @intCast(image.width),
.height = @intCast(image.height),
.data = store.images.getRGBA(image_id).?.ptr,
.mipmaps = 1,
.format = rl.PixelFormat.pixelformat_uncompressed_r8g8b8a8
});
if (!rl.isTextureReady(texture)) {
return error.LoadMapTextureFromImage;
}
map_textures.appendAssumeCapacity(MapTexture{ map_textures.appendAssumeCapacity(MapTexture{
.name = try Api.Map.Skin.fromSlice(image.code.slice()), .name = try Api.Map.Skin.fromSlice(image.code.slice()),
.texture = texture .texture = try loadTextureFromStore(store, image_id)
}); });
} }
} }
@ -107,6 +112,15 @@ pub fn init(allocator: std.mem.Allocator, store: *Api.Store, server: *Api.Server
} }
} }
var character_skin_textures = std.EnumArray(Api.Character.Skin, rl.Texture2D).initUndefined();
inline for (std.meta.fields(Api.Character.Skin)) |field| {
const skin: Api.Character.Skin = @enumFromInt(field.value);
const skin_image_id = store.images.getId(.character, skin.toString()).?;
const texture = try loadTextureFromStore(store, skin_image_id);
character_skin_textures.set(skin, texture);
}
const blur_shader = rl.loadShaderFromMemory( const blur_shader = rl.loadShaderFromMemory(
@embedFile("./base.vsh"), @embedFile("./base.vsh"),
@embedFile("./blur.fsh"), @embedFile("./blur.fsh"),
@ -124,29 +138,75 @@ pub fn init(allocator: std.mem.Allocator, store: *Api.Store, server: *Api.Server
return error.LoadFontFromMemory; return error.LoadFontFromMemory;
} }
return App{ const font_face = FontFace{ .font = font };
const ui = UI.init(font_face);
const character_id = store.characters.getId("Blondie").?;
const sim_server = Artificer.SimServer.init(0, store);
var app = try allocator.create(App);
errdefer allocator.destroy(app);
app.* = App{
.store = store, .store = store,
.server = server, .server = server,
.ui = UI.init(), .system_clock = .{},
.sim_server = sim_server,
.started_at = 0,
.sim_started_at = sim_server.clock.nanoTimestamp(),
.ui = ui,
.map_textures = map_textures, .map_textures = map_textures,
.map_texture_indexes = map_texture_indexes, .map_texture_indexes = map_texture_indexes,
.map_position_max = map_position_max, .map_position_max = map_position_max,
.map_position_min = map_position_min, .map_position_min = map_position_min,
.character_skin_textures = character_skin_textures,
.blur_shader = blur_shader, .blur_shader = blur_shader,
.font_face = .{ .font = font }, .font_face = font_face,
.camera = rl.Camera2D{ .camera = rl.Camera2D{
.offset = rl.Vector2.zero(), .offset = rl.Vector2.zero(),
.target = rl.Vector2.zero(), .target = rl.Vector2.zero(),
.rotation = 0, .rotation = 0,
.zoom = 1, .zoom = 1,
} },
.artificer = undefined,
.sim_artificer = undefined,
.artificer_thread = undefined,
}; };
app.started_at = app.system_clock.nanoTimestamp();
app.sim_artificer = try Artificer.ArtificerSim.init(allocator, store, &app.sim_server.clock, &app.sim_server, character_id);
errdefer app.sim_artificer.deinit(allocator);
app.artificer = try Artificer.ArtificerApi.init(allocator, store, &app.system_clock, server, character_id);
errdefer app.artificer.deinit(allocator);
app.artificer_thread = try std.Thread.spawn(.{ .allocator = allocator }, artificer_thread_cb, .{ app });
errdefer {
app.thread_running = false;
app.artificer_thread.join();
}
return app;
} }
pub fn deinit(self: *App) void { pub fn deinit(self: *App) void {
const allocator = self.map_textures.allocator;
self.thread_running = false;
self.artificer_thread.join();
self.artificer.deinit(allocator);
self.sim_artificer.deinit(allocator);
for (self.map_textures.items) |map_texture| { for (self.map_textures.items) |map_texture| {
map_texture.texture.unload(); map_texture.texture.unload();
} }
for (self.character_skin_textures.values) |texture| {
texture.unload();
}
self.map_textures.deinit(); self.map_textures.deinit();
self.map_texture_indexes.deinit(); self.map_texture_indexes.deinit();
@ -161,6 +221,55 @@ pub fn deinit(self: *App) void {
if (self.blur_texture_original) |render_texture| { if (self.blur_texture_original) |render_texture| {
render_texture.unload(); render_texture.unload();
} }
allocator.destroy(self);
}
fn artificer_thread_cb(app: *App) void {
while (app.thread_running) {
if (app.simulation) {
const artificer = &app.sim_artificer;
const expires_in = artificer.timeUntilCooldownExpires();
if (expires_in > 0) {
artificer.clock.sleep(expires_in);
}
artificer.tick() catch |e| {
log.err("Error in .tick in thread: {}", .{e});
app.thread_running = false;
};
} else {
const artificer = &app.artificer;
const expires_in = artificer.timeUntilCooldownExpires();
if (expires_in > 0) {
artificer.clock.sleep(expires_in);
}
artificer.tick() catch |e| {
log.err("Error in .tick in thread: {}", .{e});
app.thread_running = false;
};
}
}
}
fn loadTextureFromStore(store: *Api.Store, image_id: Api.Store.Id) !rl.Texture2D {
const image = store.images.get(image_id).?;
const texture = rl.loadTextureFromImage(rl.Image{
.width = @intCast(image.width),
.height = @intCast(image.height),
.data = store.images.getRGBA(image_id).?.ptr,
.mipmaps = 1,
.format = rl.PixelFormat.pixelformat_uncompressed_r8g8b8a8
});
if (!rl.isTextureReady(texture)) {
return error.LoadMapTextureFromImage;
}
return texture;
} }
fn cameraControls(camera: *rl.Camera2D) void { fn cameraControls(camera: *rl.Camera2D) void {
@ -243,206 +352,10 @@ fn createOrGetRenderTexture(maybe_render_texture: *?rl.RenderTexture) !rl.Render
return maybe_render_texture.*.?; return maybe_render_texture.*.?;
} }
// Modified version of `DrawRectangleRounded` where the UV texture coordiantes are consistent and align
fn drawRectangleRoundedUV(rec: rl.Rectangle, roundness: f32, color: rl.Color) void {
assert(roundness < 1);
if (roundness <= 0 or rec.width <= 1 or rec.height <= 1) {
rl.drawRectangleRec(rec, color);
return;
}
const radius: f32 = @min(rec.width, rec.height) * roundness / 2;
if (radius <= 0.0) return;
// Calculate the maximum angle between segments based on the error rate (usually 0.5f)
const smooth_circle_error_rate = 0.5;
const th: f32 = std.math.acos(2 * std.math.pow(f32, 1 - smooth_circle_error_rate / radius, 2) - 1);
var segments: i32 = @intFromFloat(@ceil(2 * std.math.pi / th) / 4.0);
if (segments <= 0) segments = 4;
const step_length = 90.0 / @as(f32, @floatFromInt(segments));
// Quick sketch to make sense of all of this,
// there are 9 parts to draw, also mark the 12 points we'll use
//
// P0____________________P1
// /| |\
// /1| 2 |3\
// P7 /__|____________________|__\ P2
// | |P8 P9| |
// | 8 | 9 | 4 |
// | __|____________________|__ |
// P6 \ |P11 P10| / P3
// \7| 6 |5/
// \|____________________|/
// P5 P4
// Coordinates of the 12 points that define the rounded rect
const radius_u = radius / rec.width;
const radius_v = radius / rec.height;
const points = [_]rl.Vector2{
.{ .x = radius_u , .y = 0 }, // P0
.{ .x = 1 - radius_u , .y = 0 }, // P1
.{ .x = 1 , .y = radius_v }, // P2
.{ .x = 1 , .y = 1 - radius_v }, // P3
.{ .x = 1 - radius_u , .y = 1 }, // P4
.{ .x = radius_u , .y = 1 }, // P5
.{ .x = 0 , .y = 1 - radius_v }, // P6
.{ .x = 0 , .y = radius_v }, // P7
.{ .x = radius_u , .y = radius_v }, // P8
.{ .x = 1 - radius_u , .y = radius_v }, // P9
.{ .x = 1 - radius_u , .y = 1 - radius_v }, // P10
.{ .x = radius_u , .y = 1 - radius_v }, // P11
};
const texture = rl.getShapesTexture();
const shape_rect = rl.getShapesTextureRectangle();
const texture_width: f32 = @floatFromInt(texture.width);
const texture_height: f32 = @floatFromInt(texture.height);
rl.gl.rlBegin(rlgl_h.RL_TRIANGLES);
defer rl.gl.rlEnd();
rl.gl.rlSetTexture(texture.id);
defer rl.gl.rlSetTexture(0);
// Draw all of the 4 corners: [1] Upper Left Corner, [3] Upper Right Corner, [5] Lower Right Corner, [7] Lower Left Corner
const centers = [_]rl.Vector2{ points[8], points[9], points[10], points[11] };
const angles = [_]f32{ 180.0, 270.0, 0.0, 90.0 };
for (0..4) |k| {
var angle = angles[k];
const center = centers[k];
for (0..@intCast(segments)) |_| {
rl.gl.rlColor4ub(color.r, color.g, color.b, color.a);
const rad_per_deg = std.math.rad_per_deg;
const triangle = .{
center,
.{
.x = center.x + @cos(rad_per_deg*(angle + step_length))*radius_u,
.y = center.y + @sin(rad_per_deg*(angle + step_length))*radius_v
},
.{
.x = center.x + @cos(rad_per_deg * angle)*radius_u,
.y = center.y + @sin(rad_per_deg * angle)*radius_v
}
};
inline for (triangle) |point| {
rl.gl.rlTexCoord2f(
(shape_rect.x + shape_rect.width * point.x) / texture_width,
(shape_rect.y + shape_rect.height * point.y) / texture_height
);
rl.gl.rlVertex2f(
rec.x + rec.width * point.x,
rec.y + rec.height * point.y
);
}
angle += step_length;
}
}
// [2] Upper Rectangle
rl.gl.rlColor4ub(color.r, color.g, color.b, color.a);
inline for (.{ 0, 8, 9, 1, 0, 9 }) |index| {
const point = points[index];
rl.gl.rlTexCoord2f(
(shape_rect.x + shape_rect.width * point.x) / texture_width,
(shape_rect.y + shape_rect.height * point.y) / texture_height
);
rl.gl.rlVertex2f(
rec.x + rec.width * point.x,
rec.y + rec.height * point.y
);
}
// [4] Right Rectangle
rl.gl.rlColor4ub(color.r, color.g, color.b, color.a);
inline for (.{ 9, 10, 3, 2, 9, 3 }) |index| {
const point = points[index];
rl.gl.rlTexCoord2f(
(shape_rect.x + shape_rect.width * point.x) / texture_width,
(shape_rect.y + shape_rect.height * point.y) / texture_height
);
rl.gl.rlVertex2f(
rec.x + rec.width * point.x,
rec.y + rec.height * point.y
);
}
// [6] Bottom Rectangle
rl.gl.rlColor4ub(color.r, color.g, color.b, color.a);
inline for (.{ 11, 5, 4, 10, 11, 4 }) |index| {
const point = points[index];
rl.gl.rlTexCoord2f(
(shape_rect.x + shape_rect.width * point.x) / texture_width,
(shape_rect.y + shape_rect.height * point.y) / texture_height
);
rl.gl.rlVertex2f(
rec.x + rec.width * point.x,
rec.y + rec.height * point.y
);
}
// [8] Left Rectangle
rl.gl.rlColor4ub(color.r, color.g, color.b, color.a);
inline for (.{ 7, 6, 11, 8, 7, 11 }) |index| {
const point = points[index];
rl.gl.rlTexCoord2f(
(shape_rect.x + shape_rect.width * point.x) / texture_width,
(shape_rect.y + shape_rect.height * point.y) / texture_height
);
rl.gl.rlVertex2f(
rec.x + rec.width * point.x,
rec.y + rec.height * point.y
);
}
// [9] Middle Rectangle
rl.gl.rlColor4ub(color.r, color.g, color.b, color.a);
inline for (.{ 8, 11, 10, 9, 8, 10 }) |index| {
const point = points[index];
rl.gl.rlTexCoord2f(
(shape_rect.x + shape_rect.width * point.x) / texture_width,
(shape_rect.y + shape_rect.height * point.y) / texture_height
);
rl.gl.rlVertex2f(
rec.x + rec.width * point.x,
rec.y + rec.height * point.y
);
}
}
fn drawBlurredWorld(self: *App, rect: rl.Rectangle, color: rl.Color) void {
const blur_both = self.blur_texture_both.?.texture;
const previous_texture = rl.getShapesTexture();
const previous_rect = rl.getShapesTextureRectangle();
defer rl.setShapesTexture(previous_texture, previous_rect);
const texture_height: f32 = @floatFromInt(blur_both.height);
const shape_rect = rl.Rectangle{
.x = rect.x,
.y = texture_height - rect.y,
.width = rect.width,
.height = -rect.height,
};
rl.setShapesTexture(blur_both, shape_rect);
const border = 2;
const roundness = 0.2;
drawRectangleRoundedUV(rect, roundness, color);
rl.drawRectangleRoundedLinesEx(RectUtils.shrink(rect, border - 1, border - 1), roundness, 0, border, srcery.bright_white.alpha(0.3));
}
pub fn drawWorld(self: *App) void { pub fn drawWorld(self: *App) void {
rl.clearBackground(srcery.black); rl.clearBackground(srcery.black);
rl.drawCircleV(rl.Vector2.zero(), 5, rl.Color.red); const tile_size = rl.Vector2.init(224, 224);
const map_size = self.map_position_max.subtract(self.map_position_min); const map_size = self.map_position_max.subtract(self.map_position_min);
for (0..@intCast(map_size.y)) |oy| { for (0..@intCast(map_size.y)) |oy| {
@ -453,11 +366,32 @@ pub fn drawWorld(self: *App) void {
const texture_index = self.map_texture_indexes.items[map_index]; const texture_index = self.map_texture_indexes.items[map_index];
const texture = self.map_textures.items[texture_index].texture; const texture = self.map_textures.items[texture_index].texture;
const tile_size = rl.Vector2.init(224, 224);
const position = rl.Vector2.init(@floatFromInt(x), @floatFromInt(y)).multiply(tile_size); const position = rl.Vector2.init(@floatFromInt(x), @floatFromInt(y)).multiply(tile_size);
rl.drawTextureV(texture, position, rl.Color.white); rl.drawTextureV(texture, position, rl.Color.white);
} }
} }
for (self.store.characters.objects.items) |optional_character| {
if (optional_character != .object) continue;
const character = optional_character.object;
const skin_texture = self.character_skin_textures.get(character.skin);
const position = rl.Vector2{
.x = @floatFromInt(character.position.x),
.y = @floatFromInt(character.position.y),
};
const skin_size = rl.Vector2{
.x = @floatFromInt(skin_texture.width),
.y = @floatFromInt(skin_texture.height),
};
rl.drawTextureV(
skin_texture,
position.addValue(0.5).multiply(tile_size).subtract(skin_size.divide(.{ .x = 2, .y = 2 })),
rl.Color.white
);
}
} }
pub fn drawWorldAndBlur(self: *App) !void { pub fn drawWorldAndBlur(self: *App) !void {
@ -490,7 +424,6 @@ pub fn drawWorldAndBlur(self: *App) !void {
const i_i32: i32 = @intCast(i); const i_i32: i32 = @intCast(i);
const io: f32 = @floatFromInt(i_i32 - kernel_radius); const io: f32 = @floatFromInt(i_i32 - kernel_radius);
kernel_coeffs[i] = @exp(-(io * io) / (sigma * sigma)); kernel_coeffs[i] = @exp(-(io * io) / (sigma * sigma));
// kernel_coeffs[i] /= @floatFromInt(kernel_coeffs.len);
} }
var kernel_sum: f32 = 0; var kernel_sum: f32 = 0;
@ -601,11 +534,6 @@ fn drawRatelimits(self: *App, box: rl.Rectangle) void {
const Category = Api.RateLimit.Category; const Category = Api.RateLimit.Category;
const ratelimits = self.server.ratelimits; const ratelimits = self.server.ratelimits;
self.drawBlurredWorld(
box,
srcery.xgray10
);
const padding = 16; const padding = 16;
var stack = UI.Stack.init(RectUtils.shrink(box, padding, padding), .top_to_bottom); var stack = UI.Stack.init(RectUtils.shrink(box, padding, padding), .top_to_bottom);
stack.gap = 8; stack.gap = 8;
@ -663,16 +591,92 @@ fn drawRatelimits(self: *App, box: rl.Rectangle) void {
} }
} }
fn showButton(self: *App, text: []const u8) UI.Interaction {
const key_hash: u16 = @truncate(@intFromPtr(text.ptr));
var button = self.ui.getOrAppendWidget(key_hash);
button.padding.vertical(8);
button.padding.horizontal(16);
button.flags.insert(.clickable);
button.size = .{
.x = .{ .text = {} },
.y = .{ .text = {} },
};
const interaction = self.ui.getInteraction(button);
var text_color: rl.Color = undefined;
if (interaction.held_down) {
button.background = .{ .color = srcery.hard_black };
text_color = srcery.white;
} else if (interaction.hovering) {
button.background = .{ .color = srcery.bright_black };
text_color = srcery.bright_white;
} else {
button.background = .{ .color = srcery.black };
text_color = srcery.bright_white;
}
button.text = .{
.content = text,
.color = text_color
};
return interaction;
}
fn showLabel(self: *App, text: []const u8) UI.Widget.Key {
const key_hash: u16 = @truncate(@intFromPtr(text.ptr));
var label = self.ui.getOrAppendWidget(key_hash);
label.text = .{
.content = text
};
label.size = .{
.x = .{ .text = {} },
.y = .{ .text = {} }
};
return label.key;
}
pub fn toggleSimulationMode(self: *App) void {
self.simulation = !self.simulation;
if (self.simulation) {
const system_clock = self.system_clock;
const time_passed = system_clock.nanoTimestamp() - self.started_at;
const sim_clock = &self.sim_server.clock;
sim_clock.timestamp_limit = self.sim_started_at + time_passed;
sim_clock.timestamp = sim_clock.timestamp_limit.?;
self.last_sim_timestamp = std.time.nanoTimestamp();
}
}
pub fn tick(self: *App) !void { pub fn tick(self: *App) !void {
for (&self.server.ratelimits.values) |*ratelimit| { for (&self.server.ratelimits.values) |*ratelimit| {
ratelimit.update_timers(std.time.milliTimestamp()); ratelimit.update_timers(std.time.milliTimestamp());
} }
if (self.simulation) {
const now = std.time.nanoTimestamp();
const time_passed = now - self.last_sim_timestamp;
self.last_sim_timestamp = now;
const sim_clock = &self.sim_server.clock;
sim_clock.timestamp_limit.? += time_passed;
}
const screen_width = rl.getScreenWidth(); const screen_width = rl.getScreenWidth();
const screen_height = rl.getScreenHeight(); const screen_height = rl.getScreenHeight();
const screen_size = rl.Vector2.init(@floatFromInt(screen_width), @floatFromInt(screen_height)); const screen_size = rl.Vector2.init(@floatFromInt(screen_width), @floatFromInt(screen_height));
if (!self.ui.isHoveringAnything()) {
cameraControls(&self.camera); cameraControls(&self.camera);
self.ui.disable_mouse_interaction = rl.isMouseButtonDown(.mouse_button_left);
} else {
self.ui.disable_mouse_interaction = false;
}
rl.beginDrawing(); rl.beginDrawing();
defer rl.endDrawing(); defer rl.endDrawing();
@ -681,9 +685,55 @@ pub fn tick(self: *App) !void {
try self.drawWorldAndBlur(); try self.drawWorldAndBlur();
self.drawRatelimits( const ui = &self.ui;
.{ .x = 20, .y = 20, .width = 200, .height = 200 }, if (self.blur_texture_both) |blur_texture_both| {
); ui.blur_background = blur_texture_both.texture;
}
ui.begin();
ui.topWidget().padding.all(16);
ui.layout().gap = 8;
{
const panel = ui.getOrAppendWidget(ui.randomWidgetHash());
panel.size = .{ .x = .fit_children, .y = .fit_children };
panel.padding.all(16);
panel.layout.gap = 8;
panel.background = .blur_world;
ui.pushWidget(panel.key);
defer ui.popWidget();
if (self.simulation) {
const clock = self.sim_server.clock;
const started_at = self.sim_started_at;
const time_passed: f32 = @floatFromInt(clock.nanoTimestamp() - started_at);
var time_passed_buff: [128]u8 = undefined;
const time_passed_str = try std.fmt.bufPrint(&time_passed_buff, "Time passed: {d:.1}s", .{ time_passed / std.time.ns_per_s });
_ = self.showLabel(time_passed_str);
if (self.showButton("Turn off simulation").clicked) {
self.toggleSimulationMode();
}
} else {
const clock = self.system_clock;
const started_at = self.started_at;
const time_passed: f32 = @floatFromInt(clock.nanoTimestamp() - started_at);
var time_passed_buff: [128]u8 = undefined;
const time_passed_str = try std.fmt.bufPrint(&time_passed_buff, "Time passed: {d:.1}s", .{ time_passed / std.time.ns_per_s });
_ = self.showLabel(time_passed_str);
if (self.showButton("Turn on simulation").clicked) {
self.toggleSimulationMode();
}
}
}
ui.end();
// self.drawRatelimits(
// .{ .x = 20, .y = 20, .width = 200, .height = 200 },
// );
rl.drawFPS( rl.drawFPS(
@as(i32, @intFromFloat(screen_size.x)) - 100, @as(i32, @intFromFloat(screen_size.x)) - 100,

View File

@ -104,6 +104,9 @@ pub fn main() anyerror!void {
try server.prefetchCached(allocator, cache_path, .{ .images = true }); try server.prefetchCached(allocator, cache_path, .{ .images = true });
} }
const characters = try server.getMyCharacters(allocator);
characters.deinit();
rl.initWindow(800, 450, "Artificer"); rl.initWindow(800, 450, "Artificer");
defer rl.closeWindow(); defer rl.closeWindow();

File diff suppressed because it is too large Load Diff

View File

@ -275,7 +275,7 @@ pub fn ArtificerType(Clock: type, Server: type) type {
return slot; return slot;
} }
fn timeUntilCooldownExpires(self: *Self) u64 { pub fn timeUntilCooldownExpires(self: *Self) u64 {
const store = self.server.store; const store = self.server.store;
const character = store.characters.get(self.character).?; const character = store.characters.get(self.character).?;

View File

@ -3,9 +3,25 @@ const std = @import("std");
const Clock = @This(); const Clock = @This();
timestamp: i128 = 0, timestamp: i128 = 0,
timestamp_limit: ?i128 = null,
pub fn sleep(self: *Clock, nanoseconds: u64) void { pub fn sleep(self: *Clock, nanoseconds: u64) void {
self.timestamp += @intCast(nanoseconds); const nanoseconds_i128: i128 = @intCast(nanoseconds);
const new_timestamp = self.timestamp + nanoseconds_i128;
if (self.timestamp_limit != null) {
while (true) {
self.timestamp = @min(self.timestamp_limit.?, new_timestamp);
if (self.timestamp < self.timestamp_limit.?) {
break;
}
std.time.sleep(std.time.ms_per_s * 100);
}
} else {
self.timestamp = new_timestamp;
}
} }
pub fn nanoTimestamp(self: Clock) i128 { pub fn nanoTimestamp(self: Clock) i128 {

View File

@ -6,7 +6,7 @@ pub fn sleep(self: *Clock, nanoseconds: u64) void {
std.time.sleep(nanoseconds); std.time.sleep(nanoseconds);
} }
pub fn nanoTimestamp(self: *Clock) i128 { pub fn nanoTimestamp(self: Clock) i128 {
_ = self; _ = self;
return std.time.nanoTimestamp(); return std.time.nanoTimestamp();
} }