mirror of
https://github.com/PixelGuys/Cubyz.git
synced 2025-08-03 11:17:05 -04:00
Rethink the allocation strategy.
Instead of one general purpose allocator per thread, there now is one stack allocator per thread, which behaves like a basic stack. Using this allocator for all the trivial allocations, makes the game overall more efficient, even if some larger allocations now require going through the global allocator. Maybe in the future the stack allocator could be extended with a temporary arena allocator functionality? This should help with #171 by almost halfing the CPU usage of the receiving network thread. However the issue remains.
This commit is contained in:
parent
498222d3a8
commit
e61709cb69
@ -23,7 +23,7 @@ pub fn readAllJsonFilesInAddons(externalAllocator: Allocator, addons: std.ArrayL
|
||||
};
|
||||
defer dir.close();
|
||||
|
||||
var walker = try dir.walk(main.threadAllocator);
|
||||
var walker = try dir.walk(main.globalAllocator);
|
||||
defer walker.deinit();
|
||||
|
||||
while(try walker.next()) |entry| {
|
||||
@ -42,8 +42,8 @@ pub fn readAllJsonFilesInAddons(externalAllocator: Allocator, addons: std.ArrayL
|
||||
|
||||
const file = try dir.dir.openFile(entry.path, .{});
|
||||
defer file.close();
|
||||
const string = try file.readToEndAlloc(main.threadAllocator, std.math.maxInt(usize));
|
||||
defer main.threadAllocator.free(string);
|
||||
const string = try file.readToEndAlloc(main.stackAllocator, std.math.maxInt(usize));
|
||||
defer main.stackAllocator.free(string);
|
||||
try output.put(id, JsonElement.parseFromString(externalAllocator, string));
|
||||
}
|
||||
}
|
||||
@ -58,7 +58,7 @@ pub fn readAllFilesInAddons(externalAllocator: Allocator, addons: std.ArrayList(
|
||||
};
|
||||
defer dir.close();
|
||||
|
||||
var walker = try dir.walk(main.threadAllocator);
|
||||
var walker = try dir.walk(main.globalAllocator);
|
||||
defer walker.deinit();
|
||||
|
||||
while(try walker.next()) |entry| {
|
||||
@ -73,9 +73,9 @@ pub fn readAllFilesInAddons(externalAllocator: Allocator, addons: std.ArrayList(
|
||||
}
|
||||
|
||||
pub fn readAssets(externalAllocator: Allocator, assetPath: []const u8, blocks: *std.StringHashMap(JsonElement), items: *std.StringHashMap(JsonElement), biomes: *std.StringHashMap(JsonElement), recipes: *std.ArrayList([]const u8)) !void {
|
||||
var addons = std.ArrayList(std.fs.Dir).init(main.threadAllocator);
|
||||
var addons = std.ArrayList(std.fs.Dir).init(main.globalAllocator);
|
||||
defer addons.deinit();
|
||||
var addonNames = std.ArrayList([]const u8).init(main.threadAllocator);
|
||||
var addonNames = std.ArrayList([]const u8).init(main.globalAllocator);
|
||||
defer addonNames.deinit();
|
||||
|
||||
{ // Find all the sub-directories to the assets folder.
|
||||
@ -85,13 +85,13 @@ pub fn readAssets(externalAllocator: Allocator, assetPath: []const u8, blocks: *
|
||||
while(try iterator.next()) |addon| {
|
||||
if(addon.kind == .directory) {
|
||||
try addons.append(try dir.dir.openDir(addon.name, .{}));
|
||||
try addonNames.append(try main.threadAllocator.dupe(u8, addon.name));
|
||||
try addonNames.append(try main.globalAllocator.dupe(u8, addon.name));
|
||||
}
|
||||
}
|
||||
}
|
||||
defer for(addons.items, addonNames.items) |*dir, addonName| {
|
||||
dir.close();
|
||||
main.threadAllocator.free(addonName);
|
||||
main.globalAllocator.free(addonName);
|
||||
};
|
||||
|
||||
try readAllJsonFilesInAddons(externalAllocator, addons, addonNames, "blocks", blocks);
|
||||
@ -152,8 +152,8 @@ pub const BlockPalette = struct {
|
||||
if(json != .JsonObject or json.JsonObject.count() == 0) {
|
||||
try self.palette.append(try allocator.dupe(u8, "cubyz:air"));
|
||||
} else {
|
||||
const palette = try main.threadAllocator.alloc(?[]const u8, json.JsonObject.count());
|
||||
defer main.threadAllocator.free(palette);
|
||||
const palette = try main.stackAllocator.alloc(?[]const u8, json.JsonObject.count());
|
||||
defer main.stackAllocator.free(palette);
|
||||
for(palette) |*val| {
|
||||
val.* = null;
|
||||
}
|
||||
@ -198,13 +198,13 @@ var loadedAssets: bool = false;
|
||||
pub fn loadWorldAssets(assetFolder: []const u8, palette: *BlockPalette) !void {
|
||||
if(loadedAssets) return; // The assets already got loaded by the server.
|
||||
loadedAssets = true;
|
||||
var blocks = try commonBlocks.cloneWithAllocator(main.threadAllocator);
|
||||
var blocks = try commonBlocks.cloneWithAllocator(main.globalAllocator);
|
||||
defer blocks.clearAndFree();
|
||||
var items = try commonItems.cloneWithAllocator(main.threadAllocator);
|
||||
var items = try commonItems.cloneWithAllocator(main.globalAllocator);
|
||||
defer items.clearAndFree();
|
||||
var biomes = try commonBiomes.cloneWithAllocator(main.threadAllocator);
|
||||
var biomes = try commonBiomes.cloneWithAllocator(main.globalAllocator);
|
||||
defer biomes.clearAndFree();
|
||||
var recipes = std.ArrayList([]const u8).init(main.threadAllocator);
|
||||
var recipes = std.ArrayList([]const u8).init(main.globalAllocator);
|
||||
try recipes.appendSlice(commonRecipes.items);
|
||||
defer recipes.clearAndFree();
|
||||
|
||||
@ -219,11 +219,11 @@ pub fn loadWorldAssets(assetFolder: []const u8, palette: *BlockPalette) !void {
|
||||
json = value;
|
||||
} else {
|
||||
std.log.err("Missing block: {s}. Replacing it with default block.", .{id});
|
||||
var map: *std.StringHashMap(JsonElement) = try main.threadAllocator.create(std.StringHashMap(JsonElement));
|
||||
map.* = std.StringHashMap(JsonElement).init(main.threadAllocator);
|
||||
var map: *std.StringHashMap(JsonElement) = try main.globalAllocator.create(std.StringHashMap(JsonElement));
|
||||
map.* = std.StringHashMap(JsonElement).init(main.globalAllocator);
|
||||
json = JsonElement{.JsonObject=map};
|
||||
}
|
||||
defer if(nullValue == null) json.free(main.threadAllocator);
|
||||
defer if(nullValue == null) json.free(main.globalAllocator);
|
||||
try registerBlock(assetFolder, id, json);
|
||||
block += 1;
|
||||
}
|
||||
|
@ -25,8 +25,8 @@ const AudioData = struct {
|
||||
const self = try main.globalAllocator.create(AudioData);
|
||||
self.* = .{.musicId = musicId};
|
||||
var err: c_int = 0;
|
||||
const path = try std.fmt.allocPrintZ(main.threadAllocator, "assets/cubyz/music/{s}.ogg", .{musicId});
|
||||
defer main.threadAllocator.free(path);
|
||||
const path = try std.fmt.allocPrintZ(main.stackAllocator, "assets/cubyz/music/{s}.ogg", .{musicId});
|
||||
defer main.stackAllocator.free(path);
|
||||
const ogg_stream = c.stb_vorbis_open_filename(path.ptr, &err, null);
|
||||
defer c.stb_vorbis_close(ogg_stream);
|
||||
if(ogg_stream != null) {
|
||||
|
@ -46,8 +46,8 @@ pub const Dir = struct {
|
||||
}
|
||||
|
||||
pub fn readToJson(self: Dir, allocator: Allocator, path: []const u8) !JsonElement {
|
||||
const string = try self.read(main.threadAllocator, path);
|
||||
defer main.threadAllocator.free(string);
|
||||
const string = try self.read(main.stackAllocator, path);
|
||||
defer main.stackAllocator.free(string);
|
||||
return JsonElement.parseFromString(allocator, string);
|
||||
}
|
||||
|
||||
@ -58,8 +58,8 @@ pub const Dir = struct {
|
||||
}
|
||||
|
||||
pub fn writeJson(self: Dir, path: []const u8, json: JsonElement) !void {
|
||||
const string = try json.toString(main.threadAllocator);
|
||||
defer main.threadAllocator.free(string);
|
||||
const string = try json.toString(main.stackAllocator);
|
||||
defer main.stackAllocator.free(string);
|
||||
try self.write(path, string);
|
||||
}
|
||||
|
||||
|
@ -391,7 +391,7 @@ pub const draw = struct {
|
||||
}
|
||||
|
||||
pub inline fn print(comptime format: []const u8, args: anytype, x: f32, y: f32, fontSize: f32, alignment: TextBuffer.Alignment) !void {
|
||||
var stackFallback = std.heap.stackFallback(4096, main.threadAllocator);
|
||||
var stackFallback = std.heap.stackFallback(4096, main.globalAllocator);
|
||||
const allocator = stackFallback.get();
|
||||
const string = try std.fmt.allocPrint(allocator, format, args);
|
||||
defer allocator.free(string);
|
||||
@ -560,7 +560,7 @@ pub const TextBuffer = struct {
|
||||
pub fn init(allocator: Allocator, text: []const u8, initialFontEffect: FontEffect, showControlCharacters: bool, alignment: Alignment) Allocator.Error!TextBuffer {
|
||||
var self: TextBuffer = undefined;
|
||||
self.alignment = alignment;
|
||||
var stackFallback = std.heap.stackFallback(4096, main.threadAllocator);
|
||||
var stackFallback = std.heap.stackFallback(4096, main.globalAllocator);
|
||||
const stackFallbackAllocator = stackFallback.get();
|
||||
// Parse the input text:
|
||||
var parser = Parser {
|
||||
@ -781,10 +781,8 @@ pub const TextBuffer = struct {
|
||||
c.glActiveTexture(c.GL_TEXTURE0);
|
||||
c.glBindTexture(c.GL_TEXTURE_2D, TextRendering.glyphTexture[0]);
|
||||
c.glBindVertexArray(draw.rectVAO);
|
||||
var stackFallback = std.heap.stackFallback(4096, main.threadAllocator);
|
||||
const allocator = stackFallback.get();
|
||||
const lineWraps: []f32 = try allocator.alloc(f32, self.lineBreaks.items.len - 1);
|
||||
defer allocator.free(lineWraps);
|
||||
const lineWraps: []f32 = try main.stackAllocator.alloc(f32, self.lineBreaks.items.len - 1);
|
||||
defer main.stackAllocator.free(lineWraps);
|
||||
var i: usize = 0;
|
||||
while(i < self.lineBreaks.items.len - 1) : (i += 1) {
|
||||
x = self.getLineOffset(i);
|
||||
@ -848,10 +846,8 @@ pub const TextBuffer = struct {
|
||||
c.glActiveTexture(c.GL_TEXTURE0);
|
||||
c.glBindTexture(c.GL_TEXTURE_2D, TextRendering.glyphTexture[0]);
|
||||
c.glBindVertexArray(draw.rectVAO);
|
||||
var stackFallback = std.heap.stackFallback(4096, main.threadAllocator);
|
||||
const allocator = stackFallback.get();
|
||||
const lineWraps: []f32 = try allocator.alloc(f32, self.lineBreaks.items.len - 1);
|
||||
defer allocator.free(lineWraps);
|
||||
const lineWraps: []f32 = try main.stackAllocator.alloc(f32, self.lineBreaks.items.len - 1);
|
||||
defer main.stackAllocator.free(lineWraps);
|
||||
var i: usize = 0;
|
||||
while(i < self.lineBreaks.items.len - 1) : (i += 1) {
|
||||
x = self.getLineOffset(i);
|
||||
@ -1043,7 +1039,7 @@ const TextRendering = struct {
|
||||
}
|
||||
|
||||
fn renderText(text: []const u8, x: f32, y: f32, fontSize: f32, initialFontEffect: TextBuffer.FontEffect, alignment: TextBuffer.Alignment) !void {
|
||||
var stackFallback = std.heap.stackFallback(4096, main.threadAllocator);
|
||||
var stackFallback = std.heap.stackFallback(4096, main.globalAllocator);
|
||||
const allocator = stackFallback.get();
|
||||
const buf = try TextBuffer.init(allocator, text, initialFontEffect, false, alignment);
|
||||
defer buf.deinit();
|
||||
@ -1076,11 +1072,11 @@ pub const Shader = struct {
|
||||
id: c_uint,
|
||||
|
||||
fn addShader(self: *const Shader, filename: []const u8, shader_stage: c_uint) !void {
|
||||
const source = main.files.read(main.threadAllocator, filename) catch |err| {
|
||||
const source = main.files.read(main.stackAllocator, filename) catch |err| {
|
||||
std.log.warn("Couldn't find file: {s}", .{filename});
|
||||
return err;
|
||||
};
|
||||
defer main.threadAllocator.free(source);
|
||||
defer main.stackAllocator.free(source);
|
||||
const shader = c.glCreateShader(shader_stage);
|
||||
defer c.glDeleteShader(shader);
|
||||
|
||||
@ -1509,7 +1505,7 @@ pub const TextureArray = struct {
|
||||
|
||||
const maxLOD = if(mipmapping) 1 + std.math.log2_int(u31, @min(maxWidth, maxHeight)) else 1;
|
||||
c.glTexStorage3D(c.GL_TEXTURE_2D_ARRAY, maxLOD, c.GL_RGBA8, maxWidth, maxHeight, @intCast(images.len));
|
||||
var arena = std.heap.ArenaAllocator.init(main.threadAllocator);
|
||||
var arena = std.heap.ArenaAllocator.init(main.globalAllocator);
|
||||
defer arena.deinit();
|
||||
const lodBuffer: [][]Color = try arena.allocator().alloc([]Color, maxLOD);
|
||||
for(lodBuffer, 0..) |*buffer, i| {
|
||||
@ -1569,8 +1565,8 @@ pub const Texture = struct {
|
||||
|
||||
pub fn initFromFile(path: []const u8) !Texture {
|
||||
const self = Texture.init();
|
||||
const image = try Image.readFromFile(main.threadAllocator, path);
|
||||
defer image.deinit(main.threadAllocator);
|
||||
const image = try Image.readFromFile(main.stackAllocator, path);
|
||||
defer image.deinit(main.stackAllocator);
|
||||
self.generate(image);
|
||||
return self;
|
||||
}
|
||||
@ -1732,19 +1728,20 @@ pub const Image = struct {
|
||||
pub fn readFromFile(allocator: Allocator, path: []const u8) !Image {
|
||||
var result: Image = undefined;
|
||||
var channel: c_int = undefined;
|
||||
const nullTerminatedPath = try std.fmt.allocPrintZ(main.threadAllocator, "{s}", .{path}); // TODO: Find a more zig-friendly image loading library.
|
||||
defer main.threadAllocator.free(nullTerminatedPath);
|
||||
const nullTerminatedPath = try std.fmt.allocPrintZ(main.stackAllocator, "{s}", .{path}); // TODO: Find a more zig-friendly image loading library.
|
||||
errdefer main.stackAllocator.free(nullTerminatedPath);
|
||||
stb_image.stbi_set_flip_vertically_on_load(1);
|
||||
const data = stb_image.stbi_load(nullTerminatedPath.ptr, @ptrCast(&result.width), @ptrCast(&result.height), &channel, 4) orelse {
|
||||
return error.FileNotFound;
|
||||
};
|
||||
main.stackAllocator.free(nullTerminatedPath);
|
||||
result.imageData = try allocator.dupe(Color, @as([*]Color, @ptrCast(data))[0..result.width*result.height]);
|
||||
stb_image.stbi_image_free(data);
|
||||
return result;
|
||||
}
|
||||
pub fn exportToFile(self: Image, path: []const u8) !void {
|
||||
const nullTerminated = try main.threadAllocator.dupeZ(u8, path);
|
||||
defer main.threadAllocator.free(nullTerminated);
|
||||
const nullTerminated = try main.stackAllocator.dupeZ(u8, path);
|
||||
defer main.stackAllocator.free(nullTerminated);
|
||||
_ = stb_image.stbi_write_png(nullTerminated.ptr, self.width, self.height, 4, self.imageData.ptr, self.width*4);
|
||||
}
|
||||
pub fn getRGB(self: Image, x: usize, y: usize) Color {
|
||||
|
@ -189,12 +189,12 @@ pub fn deinit() void {
|
||||
}
|
||||
|
||||
fn save() !void {
|
||||
const guiJson = try JsonElement.initObject(main.threadAllocator);
|
||||
defer guiJson.free(main.threadAllocator);
|
||||
const guiJson = try JsonElement.initObject(main.globalAllocator);
|
||||
defer guiJson.free(main.globalAllocator);
|
||||
for(windowList.items) |window| {
|
||||
const windowJson = try JsonElement.initObject(main.threadAllocator);
|
||||
const windowJson = try JsonElement.initObject(main.globalAllocator);
|
||||
for(window.relativePosition, 0..) |relPos, i| {
|
||||
const relPosJson = try JsonElement.initObject(main.threadAllocator);
|
||||
const relPosJson = try JsonElement.initObject(main.globalAllocator);
|
||||
switch(relPos) {
|
||||
.ratio => |ratio| {
|
||||
try relPosJson.put("type", "ratio");
|
||||
@ -227,11 +227,11 @@ fn save() !void {
|
||||
}
|
||||
|
||||
fn load() !void {
|
||||
const json: JsonElement = main.files.readToJson(main.threadAllocator, "gui_layout.json") catch |err| blk: {
|
||||
const json: JsonElement = main.files.readToJson(main.globalAllocator, "gui_layout.json") catch |err| blk: {
|
||||
if(err == error.FileNotFound) break :blk JsonElement{.JsonNull={}};
|
||||
return err;
|
||||
};
|
||||
defer json.free(main.threadAllocator);
|
||||
defer json.free(main.globalAllocator);
|
||||
|
||||
for(windowList.items) |window| {
|
||||
const windowJson = json.getChild(window.id);
|
||||
@ -664,7 +664,7 @@ pub const inventory = struct {
|
||||
if(carriedItemStack.amount == 0) if(hoveredItemSlot) |hovered| {
|
||||
if(hovered.itemStack.item) |item| {
|
||||
const tooltip = try item.getTooltip();
|
||||
var textBuffer: graphics.TextBuffer = try graphics.TextBuffer.init(main.threadAllocator, tooltip, .{}, false, .left);
|
||||
var textBuffer: graphics.TextBuffer = try graphics.TextBuffer.init(main.globalAllocator, tooltip, .{}, false, .left);
|
||||
defer textBuffer.deinit();
|
||||
var size = try textBuffer.calculateLineBreaks(16, 256);
|
||||
size[0] = 0;
|
||||
|
@ -40,6 +40,8 @@ fn flawedRender() !void {
|
||||
y += 8;
|
||||
try draw.print("Queue size: {}", .{main.threadPool.queueSize()}, 0, y, 8, .left);
|
||||
y += 8;
|
||||
try draw.print("Mesh Queue size: {}", .{main.renderer.RenderStructure.updatableList.items.len}, 0, y, 8, .left);
|
||||
y += 8;
|
||||
const faceDataSize = @sizeOf(main.chunk.meshing.FaceData);
|
||||
try draw.print("ChunkMesh memory: {} MiB / {} MiB (fragmentation: {})", .{main.chunk.meshing.faceBuffer.used*faceDataSize >> 20, main.chunk.meshing.faceBuffer.capacity*faceDataSize >> 20, main.chunk.meshing.faceBuffer.freeBlocks.items.len}, 0, y, 8, .left);
|
||||
y += 8;
|
||||
|
@ -87,8 +87,8 @@ fn onTake(recipeIndex: usize) void {
|
||||
}
|
||||
|
||||
fn findAvailableRecipes(list: *VerticalList) Allocator.Error!bool {
|
||||
const oldAmounts = try main.threadAllocator.dupe(u32, itemAmount.items);
|
||||
defer main.threadAllocator.free(oldAmounts);
|
||||
const oldAmounts = try main.stackAllocator.dupe(u32, itemAmount.items);
|
||||
defer main.stackAllocator.free(oldAmounts);
|
||||
for(itemAmount.items) |*amount| {
|
||||
amount.* = 0;
|
||||
}
|
||||
|
@ -42,11 +42,9 @@ fn discoverIpAddress() void {
|
||||
}
|
||||
|
||||
fn discoverIpAddressFromNewThread() void {
|
||||
var gpa = std.heap.GeneralPurposeAllocator(.{.thread_safe=false}){};
|
||||
main.threadAllocator = gpa.allocator();
|
||||
defer if(gpa.deinit() == .leak) {
|
||||
@panic("Memory leak");
|
||||
};
|
||||
var sta = main.utils.StackAllocator.init(main.globalAllocator, 1 << 23) catch unreachable;
|
||||
defer sta.deinit();
|
||||
main.stackAllocator = sta.allocator();
|
||||
|
||||
discoverIpAddress();
|
||||
}
|
||||
|
@ -81,8 +81,8 @@ fn deleteWorld(namePtr: usize) void {
|
||||
};
|
||||
}
|
||||
|
||||
fn parseEscapedFolderName(name: []const u8) ![]const u8 {
|
||||
var result = std.ArrayList(u8).init(main.threadAllocator);
|
||||
fn parseEscapedFolderName(allocator: std.mem.Allocator, name: []const u8) ![]const u8 {
|
||||
var result = std.ArrayList(u8).init(allocator);
|
||||
defer result.deinit();
|
||||
var i: u32 = 0;
|
||||
while(i < name.len) : (i += 1) {
|
||||
@ -127,8 +127,8 @@ pub fn onOpen() Allocator.Error!void {
|
||||
if(entry.kind == .directory) {
|
||||
const row = try HorizontalList.init();
|
||||
|
||||
const decodedName = try parseEscapedFolderName(entry.name);
|
||||
defer main.threadAllocator.free(decodedName);
|
||||
const decodedName = try parseEscapedFolderName(main.stackAllocator, entry.name);
|
||||
defer main.stackAllocator.free(decodedName);
|
||||
const name = try buttonNameArena.allocator().dupeZ(u8, entry.name); // Null terminate, so we can later recover the string from just the pointer.
|
||||
const buttonName = try std.fmt.allocPrint(buttonNameArena.allocator(), "Play {s}", .{decodedName});
|
||||
|
||||
|
@ -270,10 +270,10 @@ pub const ItemDropManager = struct {
|
||||
self.mutex.lock();
|
||||
defer self.mutex.unlock();
|
||||
if(self.size == maxCapacity) {
|
||||
const json = try itemStack.store(main.threadAllocator);
|
||||
defer json.free(main.threadAllocator);
|
||||
const string = try json.toString(main.threadAllocator);
|
||||
defer main.threadAllocator.free(string);
|
||||
const json = try itemStack.store(main.globalAllocator);
|
||||
defer json.free(main.globalAllocator);
|
||||
const string = try json.toString(main.globalAllocator);
|
||||
defer main.globalAllocator.free(string);
|
||||
std.log.err("Item drop capacitiy limit reached. Failed to add itemStack: {s}", .{string});
|
||||
if(itemStack.item) |item| {
|
||||
item.deinit();
|
||||
|
@ -442,7 +442,7 @@ const TextureGenerator = struct {
|
||||
var pixelMaterials: [16][16]PixelData = undefined;
|
||||
for(0..16) |x| {
|
||||
for(0..16) |y| {
|
||||
pixelMaterials[x][y] = PixelData.init(main.threadAllocator);
|
||||
pixelMaterials[x][y] = PixelData.init(main.globalAllocator);
|
||||
}
|
||||
}
|
||||
|
||||
@ -797,7 +797,7 @@ const ToolPhysics = struct {
|
||||
x: u8,
|
||||
y: u8,
|
||||
};
|
||||
var stack = std.ArrayList(Entry).init(main.threadAllocator);
|
||||
var stack = std.ArrayList(Entry).init(main.stackAllocator);
|
||||
defer stack.deinit();
|
||||
// Uses a simple flood-fill algorithm equivalent to light calculation.
|
||||
var x: u8 = 0;
|
||||
@ -1327,19 +1327,19 @@ pub fn register(_: []const u8, texturePath: []const u8, replacementTexturePath:
|
||||
}
|
||||
|
||||
pub fn registerRecipes(file: []const u8) !void {
|
||||
var shortcuts = std.StringHashMap(*BaseItem).init(main.threadAllocator);
|
||||
var shortcuts = std.StringHashMap(*BaseItem).init(main.globalAllocator);
|
||||
defer shortcuts.deinit();
|
||||
defer {
|
||||
var keyIterator = shortcuts.keyIterator();
|
||||
while(keyIterator.next()) |key| {
|
||||
main.threadAllocator.free(key.*);
|
||||
main.globalAllocator.free(key.*);
|
||||
}
|
||||
}
|
||||
var items = std.ArrayList(*BaseItem).init(main.threadAllocator);
|
||||
var items = std.ArrayList(*BaseItem).init(main.globalAllocator);
|
||||
defer items.deinit();
|
||||
var itemAmounts = std.ArrayList(u32).init(main.threadAllocator);
|
||||
var itemAmounts = std.ArrayList(u32).init(main.globalAllocator);
|
||||
defer itemAmounts.deinit();
|
||||
var string = std.ArrayList(u8).init(main.threadAllocator);
|
||||
var string = std.ArrayList(u8).init(main.globalAllocator);
|
||||
defer string.deinit();
|
||||
var lines = std.mem.split(u8, file, "\n");
|
||||
while(lines.next()) |line| {
|
||||
|
18
src/json.zig
18
src/json.zig
@ -222,18 +222,16 @@ pub const JsonElement = union(JsonType) {
|
||||
return self.* == .JsonNull;
|
||||
}
|
||||
|
||||
fn escape(string: []const u8, allocator: Allocator) ![]const u8 {
|
||||
var out = std.ArrayList(u8).init(allocator);
|
||||
fn escapeToWriter(writer: std.ArrayList(u8).Writer, string: []const u8) !void {
|
||||
for(string) |char| {
|
||||
switch(char) {
|
||||
'\\' => try out.appendSlice("\\\\"),
|
||||
'\n' => try out.appendSlice("\\n"),
|
||||
'\"' => try out.appendSlice("\\\""),
|
||||
'\t' => try out.appendSlice("\\t"),
|
||||
else => try out.append(char),
|
||||
'\\' => try writer.writeAll("\\\\"),
|
||||
'\n' => try writer.writeAll("\\n"),
|
||||
'\"' => try writer.writeAll("\\\""),
|
||||
'\t' => try writer.writeAll("\\t"),
|
||||
else => try writer.writeByte(char),
|
||||
}
|
||||
}
|
||||
return out.toOwnedSlice();
|
||||
}
|
||||
fn writeTabs(writer: std.ArrayList(u8).Writer, tabs: u32) !void {
|
||||
for(0..tabs) |_| {
|
||||
@ -259,11 +257,9 @@ pub const JsonElement = union(JsonType) {
|
||||
try writer.writeAll("null");
|
||||
},
|
||||
.JsonString, .JsonStringOwned => |value| {
|
||||
const escaped = try escape(value, main.threadAllocator);
|
||||
try writer.writeByte('\"');
|
||||
try writer.writeAll(escaped);
|
||||
try escapeToWriter(writer, value);
|
||||
try writer.writeByte('\"');
|
||||
main.threadAllocator.free(escaped);
|
||||
},
|
||||
.JsonArray => |array| {
|
||||
try writer.writeByte('[');
|
||||
|
30
src/main.zig
30
src/main.zig
@ -31,7 +31,7 @@ pub const c = @cImport ({
|
||||
@cInclude("GLFW/glfw3.h");
|
||||
});
|
||||
|
||||
pub threadlocal var threadAllocator: std.mem.Allocator = undefined;
|
||||
pub threadlocal var stackAllocator: std.mem.Allocator = undefined;
|
||||
pub threadlocal var seed: u64 = undefined;
|
||||
var global_gpa = std.heap.GeneralPurposeAllocator(.{.thread_safe=true}){};
|
||||
pub const globalAllocator: std.mem.Allocator = global_gpa.allocator();
|
||||
@ -178,22 +178,14 @@ pub const std_options = struct {
|
||||
};
|
||||
|
||||
fn logToFile(comptime format: []const u8, args: anytype) void {
|
||||
var stackFallbackAllocator: std.heap.StackFallbackAllocator(65536) = undefined;
|
||||
stackFallbackAllocator.fallback_allocator = threadAllocator;
|
||||
const allocator = stackFallbackAllocator.get();
|
||||
|
||||
const string = std.fmt.allocPrint(allocator, format, args) catch return;
|
||||
defer allocator.free(string);
|
||||
const string = std.fmt.allocPrint(stackAllocator, format, args) catch return;
|
||||
defer stackAllocator.free(string);
|
||||
logFile.writeAll(string) catch {};
|
||||
}
|
||||
|
||||
fn logToStdErr(comptime format: []const u8, args: anytype) void {
|
||||
var stackFallbackAllocator: std.heap.StackFallbackAllocator(65536) = undefined;
|
||||
stackFallbackAllocator.fallback_allocator = threadAllocator;
|
||||
const allocator = stackFallbackAllocator.get();
|
||||
|
||||
const string = std.fmt.allocPrint(allocator, format, args) catch return;
|
||||
defer allocator.free(string);
|
||||
const string = std.fmt.allocPrint(stackAllocator, format, args) catch return;
|
||||
defer stackAllocator.free(string);
|
||||
nosuspend std.io.getStdErr().writeAll(string) catch {};
|
||||
}
|
||||
|
||||
@ -589,8 +581,8 @@ pub const Window = struct {
|
||||
}
|
||||
|
||||
pub fn setClipboardString(string: []const u8) void {
|
||||
const nullTerminatedString = threadAllocator.dupeZ(u8, string) catch return;
|
||||
defer threadAllocator.free(nullTerminatedString);
|
||||
const nullTerminatedString = stackAllocator.dupeZ(u8, string) catch return;
|
||||
defer stackAllocator.free(nullTerminatedString);
|
||||
c.glfwSetClipboardString(window, nullTerminatedString.ptr);
|
||||
}
|
||||
|
||||
@ -667,14 +659,12 @@ pub var lastFrameTime = std.atomic.Atomic(f64).init(0);
|
||||
|
||||
pub fn main() !void {
|
||||
seed = @bitCast(std.time.milliTimestamp());
|
||||
var gpa = std.heap.GeneralPurposeAllocator(.{.thread_safe=false}){};
|
||||
threadAllocator = gpa.allocator();
|
||||
defer if(gpa.deinit() == .leak) {
|
||||
std.log.err("Memory leak", .{});
|
||||
};
|
||||
defer if(global_gpa.deinit() == .leak) {
|
||||
std.log.err("Memory leak", .{});
|
||||
};
|
||||
var sta = try utils.StackAllocator.init(globalAllocator, 1 << 23);
|
||||
defer sta.deinit();
|
||||
stackAllocator = sta.allocator();
|
||||
|
||||
// init logging.
|
||||
try std.fs.cwd().makePath("logs");
|
||||
|
@ -145,6 +145,26 @@ fn log(_x: u4, _y: u4, _z: u4) ?u4 {
|
||||
return null;
|
||||
}
|
||||
|
||||
fn sphere(_x: u4, _y: u4, _z: u4) ?u4 {
|
||||
var x = @as(f32, @floatFromInt(_x)) - 7.5;
|
||||
var y = @as(f32, @floatFromInt(_y)) - 7.5;
|
||||
var z = @as(f32, @floatFromInt(_z)) - 7.5;
|
||||
if(x*x + y*y + z*z < 8.0*8.0) {
|
||||
return 6;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
fn grass(x: u4, y: u4, z: u4) ?u4 {
|
||||
var seed = main.random.initSeed2D(542642, .{x, z});
|
||||
var val = main.random.nextFloat(&seed);
|
||||
val *= val*16;
|
||||
if(val > @as(f32, @floatFromInt(y))) {
|
||||
return 6;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
fn octahedron(_x: u4, _y: u4, _z: u4) ?u4 {
|
||||
var x = _x;
|
||||
var y = _y;
|
||||
@ -174,9 +194,9 @@ pub var fullCube: u16 = 0;
|
||||
// TODO: Allow loading from world assets.
|
||||
// TODO: Editable player models.
|
||||
pub fn init() !void {
|
||||
voxelModels = std.ArrayList(VoxelModel).init(main.threadAllocator);
|
||||
voxelModels = std.ArrayList(VoxelModel).init(main.globalAllocator);
|
||||
|
||||
nameToIndex = std.StringHashMap(u16).init(main.threadAllocator);
|
||||
nameToIndex = std.StringHashMap(u16).init(main.globalAllocator);
|
||||
|
||||
try nameToIndex.put("cube", @intCast(voxelModels.items.len));
|
||||
fullCube = @intCast(voxelModels.items.len);
|
||||
@ -193,6 +213,12 @@ pub fn init() !void {
|
||||
(try voxelModels.addOne()).init(Fence.fence3);
|
||||
(try voxelModels.addOne()).init(Fence.fence4);
|
||||
|
||||
try nameToIndex.put("sphere", @intCast(voxelModels.items.len));
|
||||
(try voxelModels.addOne()).init(sphere);
|
||||
|
||||
try nameToIndex.put("grass", @intCast(voxelModels.items.len));
|
||||
(try voxelModels.addOne()).init(grass);
|
||||
|
||||
try nameToIndex.put("octahedron", @intCast(voxelModels.items.len));
|
||||
(try voxelModels.addOne()).init(octahedron);
|
||||
|
||||
|
142
src/network.zig
142
src/network.zig
@ -94,7 +94,7 @@ const Socket = struct {
|
||||
}
|
||||
|
||||
fn resolveIP(addr: []const u8) !u32 {
|
||||
const list = try std.net.getAddressList(main.threadAllocator, addr, settings.defaultPort);
|
||||
const list = try std.net.getAddressList(main.globalAllocator, addr, settings.defaultPort);
|
||||
defer list.deinit();
|
||||
return list.addrs[0].in.sa.addr;
|
||||
}
|
||||
@ -530,11 +530,9 @@ pub const ConnectionManager = struct {
|
||||
|
||||
pub fn run(self: *ConnectionManager) !void {
|
||||
self.threadId = std.Thread.getCurrentId();
|
||||
var gpa = std.heap.GeneralPurposeAllocator(.{.thread_safe=false}){};
|
||||
main.threadAllocator = gpa.allocator();
|
||||
defer if(gpa.deinit() == .leak) {
|
||||
@panic("Memory leak");
|
||||
};
|
||||
var sta = try utils.StackAllocator.init(main.globalAllocator, 1 << 23);
|
||||
defer sta.deinit();
|
||||
main.stackAllocator = sta.allocator();
|
||||
|
||||
var lastTime = std.time.milliTimestamp();
|
||||
while(self.running.load(.Monotonic)) {
|
||||
@ -605,19 +603,19 @@ pub const Protocols = struct {
|
||||
conn.handShakeState.store(data[0], .Monotonic);
|
||||
switch(data[0]) {
|
||||
stepUserData => {
|
||||
const json = JsonElement.parseFromString(main.threadAllocator, data[1..]);
|
||||
defer json.free(main.threadAllocator);
|
||||
const json = JsonElement.parseFromString(main.globalAllocator, data[1..]);
|
||||
defer json.free(main.globalAllocator);
|
||||
const name = json.get([]const u8, "name", "unnamed");
|
||||
const version = json.get([]const u8, "version", "unknown");
|
||||
std.log.info("User {s} joined using version {s}.", .{name, version});
|
||||
|
||||
{
|
||||
// TODO: Send the world data.
|
||||
const path = try std.fmt.allocPrint(main.threadAllocator, "saves/{s}/assets/", .{"Development"}); // TODO: Use world name.
|
||||
defer main.threadAllocator.free(path);
|
||||
const path = try std.fmt.allocPrint(main.stackAllocator, "saves/{s}/assets/", .{"Development"}); // TODO: Use world name.
|
||||
defer main.stackAllocator.free(path);
|
||||
var dir = try std.fs.cwd().openIterableDir(path, .{});
|
||||
defer dir.close();
|
||||
var arrayList = std.ArrayList(u8).init(main.threadAllocator);
|
||||
var arrayList = std.ArrayList(u8).init(main.globalAllocator);
|
||||
defer arrayList.deinit();
|
||||
try arrayList.append(stepAssets);
|
||||
try utils.Compression.pack(dir, arrayList.writer());
|
||||
@ -627,20 +625,20 @@ pub const Protocols = struct {
|
||||
|
||||
// TODO:
|
||||
try conn.user.?.initPlayer(name);
|
||||
const jsonObject = try JsonElement.initObject(main.threadAllocator);
|
||||
defer jsonObject.free(main.threadAllocator);
|
||||
try jsonObject.put("player", try conn.user.?.player.save(main.threadAllocator));
|
||||
const jsonObject = try JsonElement.initObject(main.globalAllocator);
|
||||
defer jsonObject.free(main.globalAllocator);
|
||||
try jsonObject.put("player", try conn.user.?.player.save(main.globalAllocator));
|
||||
// TODO:
|
||||
// jsonObject.put("player_id", ((User)conn).player.id);
|
||||
// jsonObject.put("blockPalette", Server.world.blockPalette.save());
|
||||
const spawn = try JsonElement.initObject(main.threadAllocator);
|
||||
const spawn = try JsonElement.initObject(main.globalAllocator);
|
||||
try spawn.put("x", main.server.world.?.spawn[0]);
|
||||
try spawn.put("y", main.server.world.?.spawn[1]);
|
||||
try spawn.put("z", main.server.world.?.spawn[2]);
|
||||
try jsonObject.put("spawn", spawn);
|
||||
|
||||
const outData = try jsonObject.toStringEfficient(main.threadAllocator, &[1]u8{stepServerData});
|
||||
defer main.threadAllocator.free(outData);
|
||||
const outData = try jsonObject.toStringEfficient(main.stackAllocator, &[1]u8{stepServerData});
|
||||
defer main.stackAllocator.free(outData);
|
||||
try conn.sendImportant(id, outData);
|
||||
conn.handShakeState.store(stepServerData, .Monotonic);
|
||||
conn.handShakeState.store(stepComplete, .Monotonic);
|
||||
@ -656,8 +654,8 @@ pub const Protocols = struct {
|
||||
try utils.Compression.unpack(try std.fs.cwd().openDir("serverAssets", .{}), data[1..]);
|
||||
},
|
||||
stepServerData => {
|
||||
const json = JsonElement.parseFromString(main.threadAllocator, data[1..]);
|
||||
defer json.free(main.threadAllocator);
|
||||
const json = JsonElement.parseFromString(main.globalAllocator, data[1..]);
|
||||
defer json.free(main.globalAllocator);
|
||||
try conn.manager.world.?.finishHandshake(json);
|
||||
conn.handShakeState.store(stepComplete, .Monotonic);
|
||||
conn.handShakeWaiting.broadcast(); // Notify the waiting client thread.
|
||||
@ -679,14 +677,14 @@ pub const Protocols = struct {
|
||||
}
|
||||
|
||||
pub fn clientSide(conn: *Connection, name: []const u8) !void {
|
||||
const jsonObject = JsonElement{.JsonObject=try main.threadAllocator.create(std.StringHashMap(JsonElement))};
|
||||
defer jsonObject.free(main.threadAllocator);
|
||||
jsonObject.JsonObject.* = std.StringHashMap(JsonElement).init(main.threadAllocator);
|
||||
const jsonObject = JsonElement{.JsonObject=try main.globalAllocator.create(std.StringHashMap(JsonElement))};
|
||||
defer jsonObject.free(main.globalAllocator);
|
||||
jsonObject.JsonObject.* = std.StringHashMap(JsonElement).init(main.globalAllocator);
|
||||
try jsonObject.putOwnedString("version", settings.version);
|
||||
try jsonObject.putOwnedString("name", name);
|
||||
const prefix = [1]u8 {stepUserData};
|
||||
const data = try jsonObject.toStringEfficient(main.threadAllocator, &prefix);
|
||||
defer main.threadAllocator.free(data);
|
||||
const data = try jsonObject.toStringEfficient(main.stackAllocator, &prefix);
|
||||
defer main.stackAllocator.free(data);
|
||||
try conn.sendImportant(id, data);
|
||||
|
||||
conn.mutex.lock();
|
||||
@ -713,8 +711,8 @@ pub const Protocols = struct {
|
||||
}
|
||||
pub fn sendRequest(conn: *Connection, requests: []chunk.ChunkPosition) !void {
|
||||
if(requests.len == 0) return;
|
||||
const data = try main.threadAllocator.alloc(u8, 16*requests.len);
|
||||
defer main.threadAllocator.free(data);
|
||||
const data = try main.stackAllocator.alloc(u8, 16*requests.len);
|
||||
defer main.stackAllocator.free(data);
|
||||
var remaining = data;
|
||||
for(requests) |req| {
|
||||
std.mem.writeInt(i32, remaining[0..4], req.wx, .big);
|
||||
@ -736,8 +734,8 @@ pub const Protocols = struct {
|
||||
.wz = std.mem.readInt(i32, data[8..12], .big),
|
||||
.voxelSize = @intCast(std.mem.readInt(i32, data[12..16], .big)),
|
||||
};
|
||||
const _inflatedData = try main.threadAllocator.alloc(u8, chunk.chunkVolume*4);
|
||||
defer main.threadAllocator.free(_inflatedData);
|
||||
const _inflatedData = try main.stackAllocator.alloc(u8, chunk.chunkVolume*4);
|
||||
defer main.stackAllocator.free(_inflatedData);
|
||||
const _inflatedLen = try utils.Compression.inflateTo(_inflatedData, data[16..]);
|
||||
if(_inflatedLen != chunk.chunkVolume*4) {
|
||||
std.log.err("Transmission of chunk has invalid size: {}. Input data: {any}, After inflate: {any}", .{_inflatedLen, data, _inflatedData[0.._inflatedLen]});
|
||||
@ -756,10 +754,10 @@ pub const Protocols = struct {
|
||||
for(&ch.blocks, 0..) |*block, i| {
|
||||
std.mem.writeInt(u32, uncompressedData[4*i..][0..4], block.toInt(), .big);
|
||||
}
|
||||
const compressedData = try utils.Compression.deflate(main.threadAllocator, &uncompressedData);
|
||||
defer main.threadAllocator.free(compressedData);
|
||||
const data =try main.threadAllocator.alloc(u8, 16 + compressedData.len);
|
||||
defer main.threadAllocator.free(data);
|
||||
const compressedData = try utils.Compression.deflate(main.stackAllocator, &uncompressedData);
|
||||
defer main.stackAllocator.free(compressedData);
|
||||
const data = try main.stackAllocator.alloc(u8, 16 + compressedData.len);
|
||||
defer main.stackAllocator.free(data);
|
||||
@memcpy(data[16..], compressedData);
|
||||
std.mem.writeInt(i32, data[0..4], ch.pos.wx, .big);
|
||||
std.mem.writeInt(i32, data[4..8], ch.pos.wy, .big);
|
||||
@ -818,15 +816,15 @@ pub const Protocols = struct {
|
||||
}
|
||||
}
|
||||
pub fn send(conn: *Connection, entityData: []const u8, itemData: []const u8) !void {
|
||||
const fullEntityData = main.threadAllocator.alloc(u8, entityData.len + 3);
|
||||
defer main.threadAllocator.free(fullEntityData);
|
||||
const fullEntityData = main.stackAllocator.alloc(u8, entityData.len + 3);
|
||||
defer main.stackAllocator.free(fullEntityData);
|
||||
fullEntityData[0] = type_entity;
|
||||
std.mem.writeInt(i16, fullEntityData[1..3], @as(i16, @truncate(std.time.milliTimestamp())));
|
||||
@memcpy(fullEntityData[3..], entityData);
|
||||
conn.sendUnimportant(id, fullEntityData);
|
||||
|
||||
const fullItemData = main.threadAllocator.alloc(u8, itemData.len + 3);
|
||||
defer main.threadAllocator.free(fullItemData);
|
||||
const fullItemData = main.stackAllocator.alloc(u8, itemData.len + 3);
|
||||
defer main.stackAllocator.free(fullItemData);
|
||||
fullItemData[0] = type_item;
|
||||
std.mem.writeInt(i16, fullItemData[1..3], @as(i16, @truncate(std.time.milliTimestamp())));
|
||||
@memcpy(fullItemData[3..], itemData);
|
||||
@ -858,8 +856,8 @@ pub const Protocols = struct {
|
||||
pub const entity = struct {
|
||||
const id: u8 = 8;
|
||||
fn receive(conn: *Connection, data: []const u8) !void {
|
||||
const jsonArray = JsonElement.parseFromString(main.threadAllocator, data);
|
||||
defer jsonArray.free(main.threadAllocator);
|
||||
const jsonArray = JsonElement.parseFromString(main.globalAllocator, data);
|
||||
defer jsonArray.free(main.globalAllocator);
|
||||
var i: u32 = 0;
|
||||
while(i < jsonArray.JsonArray.items.len) : (i += 1) {
|
||||
const elem = jsonArray.JsonArray.items[i];
|
||||
@ -1054,8 +1052,8 @@ pub const Protocols = struct {
|
||||
// );
|
||||
},
|
||||
type_itemStackCollect => {
|
||||
const json = JsonElement.parseFromString(main.threadAllocator, data[1..]);
|
||||
defer json.free(main.threadAllocator);
|
||||
const json = JsonElement.parseFromString(main.globalAllocator, data[1..]);
|
||||
defer json.free(main.globalAllocator);
|
||||
const item = items.Item.init(json) catch |err| {
|
||||
std.log.err("Error {s} while collecting item {s}. Ignoring it.", .{@errorName(err), data[1..]});
|
||||
return;
|
||||
@ -1072,8 +1070,8 @@ pub const Protocols = struct {
|
||||
},
|
||||
type_timeAndBiome => {
|
||||
if(conn.manager.world) |world| {
|
||||
const json = JsonElement.parseFromString(main.threadAllocator, data[1..]);
|
||||
defer json.free(main.threadAllocator);
|
||||
const json = JsonElement.parseFromString(main.globalAllocator, data[1..]);
|
||||
defer json.free(main.globalAllocator);
|
||||
const expectedTime = json.get(i64, "time", 0);
|
||||
var curTime = world.gameTime.load(.Monotonic);
|
||||
if(@abs(curTime -% expectedTime) >= 1000) {
|
||||
@ -1097,16 +1095,16 @@ pub const Protocols = struct {
|
||||
}
|
||||
|
||||
fn addHeaderAndSendImportant(conn: *Connection, header: u8, data: []const u8) !void {
|
||||
const headeredData = try main.threadAllocator.alloc(u8, data.len + 1);
|
||||
defer main.threadAllocator.free(headeredData);
|
||||
const headeredData = try main.stackAllocator.alloc(u8, data.len + 1);
|
||||
defer main.stackAllocator.free(headeredData);
|
||||
headeredData[0] = header;
|
||||
@memcpy(headeredData[1..], data);
|
||||
try conn.sendImportant(id, headeredData);
|
||||
}
|
||||
|
||||
fn addHeaderAndSendUnimportant(conn: *Connection, header: u8, data: []const u8) !void {
|
||||
const headeredData = try main.threadAllocator.alloc(u8, data.len + 1);
|
||||
defer main.threadAllocator.free(headeredData);
|
||||
const headeredData = try main.stackAllocator.alloc(u8, data.len + 1);
|
||||
defer main.stackAllocator.free(headeredData);
|
||||
headeredData[0] = header;
|
||||
@memcpy(headeredData[1..], data);
|
||||
try conn.sendUnimportant(id, headeredData);
|
||||
@ -1144,10 +1142,10 @@ pub const Protocols = struct {
|
||||
|
||||
|
||||
pub fn sendInventory_full(conn: *Connection, inv: Inventory) !void {
|
||||
const json = try inv.save(main.threadAllocator);
|
||||
defer json.free(main.threadAllocator);
|
||||
const string = try json.toString(main.threadAllocator);
|
||||
defer main.threadAllocator.free(string);
|
||||
const json = try inv.save(main.globalAllocator);
|
||||
defer json.free(main.globalAllocator);
|
||||
const string = try json.toString(main.stackAllocator);
|
||||
defer main.stackAllocator.free(string);
|
||||
try addHeaderAndSendImportant(conn, type_inventoryFull, string);
|
||||
}
|
||||
|
||||
@ -1158,8 +1156,8 @@ pub const Protocols = struct {
|
||||
}
|
||||
|
||||
pub fn itemStackDrop(conn: *Connection, stack: ItemStack, pos: Vec3d, dir: Vec3f, vel: f32) !void {
|
||||
const jsonObject = try stack.store(main.threadAllocator);
|
||||
defer jsonObject.free(main.threadAllocator);
|
||||
const jsonObject = try stack.store(main.globalAllocator);
|
||||
defer jsonObject.free(main.globalAllocator);
|
||||
try jsonObject.put("x", pos[0]);
|
||||
try jsonObject.put("y", pos[1]);
|
||||
try jsonObject.put("z", pos[2]);
|
||||
@ -1167,27 +1165,27 @@ pub const Protocols = struct {
|
||||
try jsonObject.put("dirY", dir[1]);
|
||||
try jsonObject.put("dirZ", dir[2]);
|
||||
try jsonObject.put("vel", vel);
|
||||
const string = try jsonObject.toString(main.threadAllocator);
|
||||
defer main.threadAllocator.free(string);
|
||||
const string = try jsonObject.toString(main.stackAllocator);
|
||||
defer main.stackAllocator.free(string);
|
||||
try addHeaderAndSendImportant(conn, type_itemStackDrop, string);
|
||||
}
|
||||
|
||||
pub fn itemStackCollect(conn: *Connection, stack: ItemStack) !void {
|
||||
const json = try stack.store(main.threadAllocator);
|
||||
defer json.free(main.threadAllocator);
|
||||
const string = try json.toString(main.threadAllocator);
|
||||
defer main.threadAllocator.free(string);
|
||||
const json = try stack.store(main.globalAllocator);
|
||||
defer json.free(main.globalAllocator);
|
||||
const string = try json.toString(main.stackAllocator);
|
||||
defer main.stackAllocator.free(string);
|
||||
try addHeaderAndSendImportant(conn, type_itemStackCollect, string);
|
||||
}
|
||||
|
||||
pub fn sendTimeAndBiome(conn: *Connection, world: *const main.server.ServerWorld) !void {
|
||||
const json = try JsonElement.initObject(main.threadAllocator);
|
||||
defer json.free(main.threadAllocator);
|
||||
const json = try JsonElement.initObject(main.globalAllocator);
|
||||
defer json.free(main.globalAllocator);
|
||||
try json.put("time", world.gameTime);
|
||||
const pos = conn.user.?.player.pos;
|
||||
try json.put("biome", (try world.getBiome(@intFromFloat(pos[0]), @intFromFloat(pos[1]), @intFromFloat(pos[2]))).id);
|
||||
const string = try json.toString(main.threadAllocator);
|
||||
defer main.threadAllocator.free(string);
|
||||
const string = try json.toString(main.stackAllocator);
|
||||
defer main.stackAllocator.free(string);
|
||||
try addHeaderAndSendUnimportant(conn, type_timeAndBiome, string);
|
||||
}
|
||||
};
|
||||
@ -1199,8 +1197,8 @@ pub const Protocols = struct {
|
||||
// TODO:
|
||||
// CommandExecutor.execute(data, user);
|
||||
} else {
|
||||
const newMessage = try std.fmt.allocPrint(main.threadAllocator, "[{s}#ffffff]{s}", .{user.name, data});
|
||||
defer main.threadAllocator.free(newMessage);
|
||||
const newMessage = try std.fmt.allocPrint(main.stackAllocator, "[{s}#ffffff]{s}", .{user.name, data});
|
||||
defer main.stackAllocator.free(newMessage);
|
||||
main.server.mutex.lock();
|
||||
defer main.server.mutex.unlock();
|
||||
try main.server.sendMessage(newMessage);
|
||||
@ -1363,8 +1361,8 @@ pub const Connection = struct {
|
||||
|
||||
if(self.disconnected) return;
|
||||
std.debug.assert(data.len + 1 < maxPacketSize);
|
||||
const fullData = try main.threadAllocator.alloc(u8, data.len + 1);
|
||||
defer main.threadAllocator.free(fullData);
|
||||
const fullData = try main.stackAllocator.alloc(u8, data.len + 1);
|
||||
defer main.stackAllocator.free(fullData);
|
||||
fullData[0] = id;
|
||||
@memcpy(fullData[1..], data);
|
||||
try self.manager.send(fullData, self.remoteAddress);
|
||||
@ -1400,9 +1398,9 @@ pub const Connection = struct {
|
||||
self.mutex.lock();
|
||||
defer self.mutex.unlock();
|
||||
|
||||
var runLengthEncodingStarts: std.ArrayList(u32) = std.ArrayList(u32).init(main.threadAllocator);
|
||||
var runLengthEncodingStarts: std.ArrayList(u32) = std.ArrayList(u32).init(main.globalAllocator);
|
||||
defer runLengthEncodingStarts.deinit();
|
||||
var runLengthEncodingLengths: std.ArrayList(u32) = std.ArrayList(u32).init(main.threadAllocator);
|
||||
var runLengthEncodingLengths: std.ArrayList(u32) = std.ArrayList(u32).init(main.globalAllocator);
|
||||
defer runLengthEncodingLengths.deinit();
|
||||
|
||||
for(self.receivedPackets) |list| {
|
||||
@ -1446,8 +1444,8 @@ pub const Connection = struct {
|
||||
self.receivedPackets[0] = putBackToFront;
|
||||
self.receivedPackets[0].clearRetainingCapacity();
|
||||
}
|
||||
const output = try main.threadAllocator.alloc(u8, runLengthEncodingStarts.items.len*8 + 9);
|
||||
defer main.threadAllocator.free(output);
|
||||
const output = try main.stackAllocator.alloc(u8, runLengthEncodingStarts.items.len*8 + 9);
|
||||
defer main.stackAllocator.free(output);
|
||||
output[0] = Protocols.keepAlive;
|
||||
std.mem.writeInt(u32, output[1..5], self.lastKeepAliveSent, .big);
|
||||
self.lastKeepAliveSent += 1;
|
||||
@ -1531,8 +1529,8 @@ pub const Connection = struct {
|
||||
}
|
||||
|
||||
// Copy the data to an array:
|
||||
const data = try main.threadAllocator.alloc(u8, len);
|
||||
defer main.threadAllocator.free(data);
|
||||
const data = try main.stackAllocator.alloc(u8, len);
|
||||
defer main.stackAllocator.free(data);
|
||||
var remaining = data[0..];
|
||||
while(remaining.len != 0) {
|
||||
dataAvailable = @min(self.lastReceivedPackets[id & 65535].?.len - newIndex, remaining.len);
|
||||
|
@ -486,19 +486,19 @@ pub const MenuBackGround = struct {
|
||||
var dir: std.fs.IterableDir = try std.fs.cwd().makeOpenPathIterable("assets/backgrounds", .{});
|
||||
defer dir.close();
|
||||
|
||||
var walker = try dir.walk(main.threadAllocator);
|
||||
var walker = try dir.walk(main.globalAllocator);
|
||||
defer walker.deinit();
|
||||
var fileList = std.ArrayList([]const u8).init(main.threadAllocator);
|
||||
var fileList = std.ArrayList([]const u8).init(main.globalAllocator);
|
||||
defer {
|
||||
for(fileList.items) |fileName| {
|
||||
main.threadAllocator.free(fileName);
|
||||
main.globalAllocator.free(fileName);
|
||||
}
|
||||
fileList.deinit();
|
||||
}
|
||||
|
||||
while(try walker.next()) |entry| {
|
||||
if(entry.kind == .file and std.ascii.endsWithIgnoreCase(entry.basename, ".png")) {
|
||||
try fileList.append(try main.threadAllocator.dupe(u8, entry.path));
|
||||
try fileList.append(try main.globalAllocator.dupe(u8, entry.path));
|
||||
}
|
||||
}
|
||||
if(fileList.items.len == 0) {
|
||||
@ -507,8 +507,8 @@ pub const MenuBackGround = struct {
|
||||
return;
|
||||
}
|
||||
const theChosenOne = main.random.nextIntBounded(u32, &main.seed, @as(u32, @intCast(fileList.items.len)));
|
||||
const theChosenPath = try std.fmt.allocPrint(main.threadAllocator, "assets/backgrounds/{s}", .{fileList.items[theChosenOne]});
|
||||
defer main.threadAllocator.free(theChosenPath);
|
||||
const theChosenPath = try std.fmt.allocPrint(main.stackAllocator, "assets/backgrounds/{s}", .{fileList.items[theChosenOne]});
|
||||
defer main.stackAllocator.free(theChosenPath);
|
||||
texture = try graphics.Texture.initFromFile(theChosenPath);
|
||||
}
|
||||
|
||||
@ -544,8 +544,8 @@ pub const MenuBackGround = struct {
|
||||
|
||||
pub fn takeBackgroundImage() !void {
|
||||
const size: usize = 1024; // Use a power of 2 here, to reduce video memory waste.
|
||||
const pixels: []u32 = try main.threadAllocator.alloc(u32, size*size);
|
||||
defer main.threadAllocator.free(pixels);
|
||||
const pixels: []u32 = try main.stackAllocator.alloc(u32, size*size);
|
||||
defer main.stackAllocator.free(pixels);
|
||||
|
||||
// Change the viewport and the matrices to render 4 cube faces:
|
||||
|
||||
@ -566,8 +566,8 @@ pub const MenuBackGround = struct {
|
||||
const angles = [_]f32 {std.math.pi/2.0, std.math.pi, std.math.pi*3/2.0, std.math.pi*2};
|
||||
|
||||
// All 4 sides are stored in a single image.
|
||||
const image = try graphics.Image.init(main.threadAllocator, 4*size, size);
|
||||
defer image.deinit(main.threadAllocator);
|
||||
const image = try graphics.Image.init(main.stackAllocator, 4*size, size);
|
||||
defer image.deinit(main.stackAllocator);
|
||||
|
||||
for(0..4) |i| {
|
||||
c.glEnable(c.GL_CULL_FACE);
|
||||
@ -593,8 +593,8 @@ pub const MenuBackGround = struct {
|
||||
c.glDisable(c.GL_DEPTH_TEST);
|
||||
c.glBindFramebuffer(c.GL_FRAMEBUFFER, 0);
|
||||
|
||||
const fileName = try std.fmt.allocPrint(main.threadAllocator, "assets/backgrounds/{s}_{}.png", .{game.world.?.name, game.world.?.gameTime.load(.Monotonic)});
|
||||
defer main.threadAllocator.free(fileName);
|
||||
const fileName = try std.fmt.allocPrint(main.stackAllocator, "assets/backgrounds/{s}_{}.png", .{game.world.?.name, game.world.?.gameTime.load(.Monotonic)});
|
||||
defer main.stackAllocator.free(fileName);
|
||||
try image.exportToFile(fileName);
|
||||
// TODO: Performance is terrible even with -O3. Consider using qoi instead.
|
||||
}
|
||||
@ -880,7 +880,7 @@ pub const RenderStructure = struct {
|
||||
var storageLists: [settings.highestLOD + 1]*[storageSize*storageSize*storageSize]ChunkMeshNode = undefined;
|
||||
var meshList = std.ArrayList(*chunk.meshing.ChunkMesh).init(main.globalAllocator);
|
||||
var priorityUpdateList = std.ArrayList(*chunk.meshing.ChunkMesh).init(main.globalAllocator);
|
||||
var updatableList = std.ArrayList(*chunk.meshing.ChunkMesh).init(main.globalAllocator);
|
||||
pub var updatableList = std.ArrayList(*chunk.meshing.ChunkMesh).init(main.globalAllocator);
|
||||
var clearList = std.ArrayList(*chunk.meshing.ChunkMesh).init(main.globalAllocator);
|
||||
var lastPx: i32 = 0;
|
||||
var lastPy: i32 = 0;
|
||||
@ -1183,7 +1183,7 @@ pub const RenderStructure = struct {
|
||||
const py: i32 = @intFromFloat(playerPos[1]);
|
||||
const pz: i32 = @intFromFloat(playerPos[2]);
|
||||
|
||||
var meshRequests = std.ArrayList(chunk.ChunkPosition).init(main.threadAllocator);
|
||||
var meshRequests = std.ArrayList(chunk.ChunkPosition).init(main.globalAllocator);
|
||||
defer meshRequests.deinit();
|
||||
|
||||
try freeOldMeshes(px, py, pz, renderDistance);
|
||||
@ -1203,7 +1203,7 @@ pub const RenderStructure = struct {
|
||||
};
|
||||
|
||||
// TODO: Is there a way to combine this with minecraft's approach?
|
||||
var searchList = std.PriorityQueue(OcclusionData, void, OcclusionData.compare).init(main.threadAllocator, {});
|
||||
var searchList = std.PriorityQueue(OcclusionData, void, OcclusionData.compare).init(main.globalAllocator, {});
|
||||
defer searchList.deinit();
|
||||
{
|
||||
var firstPos = chunk.ChunkPosition{
|
||||
|
@ -153,11 +153,9 @@ fn update() !void {
|
||||
}
|
||||
|
||||
pub fn start(name: []const u8) !void {
|
||||
var gpa = std.heap.GeneralPurposeAllocator(.{.thread_safe=false}){};
|
||||
main.threadAllocator = gpa.allocator();
|
||||
defer if(gpa.deinit() == .leak) {
|
||||
std.log.err("Memory leak", .{});
|
||||
};
|
||||
var sta = try utils.StackAllocator.init(main.globalAllocator, 1 << 23);
|
||||
defer sta.deinit();
|
||||
main.stackAllocator = sta.allocator();
|
||||
std.debug.assert(!running.load(.Monotonic)); // There can only be one server.
|
||||
try init(name);
|
||||
defer deinit();
|
||||
@ -182,8 +180,8 @@ pub fn stop() void {
|
||||
|
||||
pub fn disconnect(user: *User) !void {
|
||||
// TODO: world.forceSave();
|
||||
const message = try std.fmt.allocPrint(main.threadAllocator, "{s} #ffff00left", .{user.name});
|
||||
defer main.threadAllocator.free(message);
|
||||
const message = try std.fmt.allocPrint(main.stackAllocator, "{s} #ffff00left", .{user.name});
|
||||
defer main.stackAllocator.free(message);
|
||||
mutex.lock();
|
||||
defer mutex.unlock();
|
||||
try sendMessage(message);
|
||||
@ -199,8 +197,8 @@ pub fn disconnect(user: *User) !void {
|
||||
}
|
||||
|
||||
pub fn connect(user: *User) !void {
|
||||
const message = try std.fmt.allocPrint(main.threadAllocator, "{s} #ffff00joined", .{user.name});
|
||||
defer main.threadAllocator.free(message);
|
||||
const message = try std.fmt.allocPrint(main.stackAllocator, "{s} #ffff00joined", .{user.name});
|
||||
defer main.stackAllocator.free(message);
|
||||
mutex.lock();
|
||||
defer mutex.unlock();
|
||||
try sendMessage(message);
|
||||
|
@ -146,10 +146,10 @@ pub const Biome = struct {
|
||||
|
||||
const structures = json.getChild("structures");
|
||||
var vegetation = std.ArrayListUnmanaged(StructureModel){};
|
||||
defer vegetation.deinit(main.threadAllocator);
|
||||
defer vegetation.deinit(main.globalAllocator);
|
||||
for(structures.toSlice()) |elem| {
|
||||
if(try StructureModel.initModel(elem)) |model| {
|
||||
try vegetation.append(main.threadAllocator, model);
|
||||
try vegetation.append(main.globalAllocator, model);
|
||||
}
|
||||
}
|
||||
self.vegetationModels = try main.globalAllocator.dupe(StructureModel, vegetation.items);
|
||||
|
@ -29,8 +29,8 @@ pub fn deinit() void {
|
||||
|
||||
pub fn generate(map: *CaveBiomeMapFragment, worldSeed: u64) Allocator.Error!void {
|
||||
// Select all the biomes that are within the given height range.
|
||||
var validBiomes = try std.ArrayListUnmanaged(*const Biome).initCapacity(main.threadAllocator, caveBiomes.len);
|
||||
defer validBiomes.deinit(main.threadAllocator);
|
||||
var validBiomes = try std.ArrayListUnmanaged(*const Biome).initCapacity(main.stackAllocator, caveBiomes.len);
|
||||
defer validBiomes.deinit(main.stackAllocator);
|
||||
for(caveBiomes) |*biome| {
|
||||
if(biome.minHeight < map.pos.wy +% CaveBiomeMapFragment.caveBiomeMapSize and biome.maxHeight > map.pos.wy) {
|
||||
validBiomes.appendAssumeCapacity(biome);
|
||||
|
@ -43,8 +43,8 @@ pub fn generate(map: *CaveMapFragment, worldSeed: u64) Allocator.Error!void {
|
||||
const outerSize = @max(map.pos.voxelSize, interpolatedPart);
|
||||
const outerSizeShift = std.math.log2_int(u31, outerSize);
|
||||
const outerSizeFloat: f32 = @floatFromInt(outerSize);
|
||||
const noise = try FractalNoise3D.generateAligned(main.threadAllocator, map.pos.wx, map.pos.wy, map.pos.wz, outerSize, CaveMapFragment.width*map.pos.voxelSize/outerSize + 1, CaveMapFragment.height*map.pos.voxelSize/outerSize + 1, CaveMapFragment.width*map.pos.voxelSize/outerSize + 1, worldSeed, scale);//try Cached3DFractalNoise.init(map.pos.wx, map.pos.wy & ~@as(i32, CaveMapFragment.width*map.pos.voxelSize - 1), map.pos.wz, outerSize, map.pos.voxelSize*CaveMapFragment.width, worldSeed, scale);
|
||||
defer noise.deinit(main.threadAllocator);
|
||||
const noise = try FractalNoise3D.generateAligned(main.stackAllocator, map.pos.wx, map.pos.wy, map.pos.wz, outerSize, CaveMapFragment.width*map.pos.voxelSize/outerSize + 1, CaveMapFragment.height*map.pos.voxelSize/outerSize + 1, CaveMapFragment.width*map.pos.voxelSize/outerSize + 1, worldSeed, scale);//try Cached3DFractalNoise.init(map.pos.wx, map.pos.wy & ~@as(i32, CaveMapFragment.width*map.pos.voxelSize - 1), map.pos.wz, outerSize, map.pos.voxelSize*CaveMapFragment.width, worldSeed, scale);
|
||||
defer noise.deinit(main.stackAllocator);
|
||||
biomeMap.bulkInterpolateValue("caves", map.pos.wx, map.pos.wy, map.pos.wz, outerSize, noise, .addToMap, scale);
|
||||
var x: u31 = 0;
|
||||
while(x < map.pos.voxelSize*CaveMapFragment.width) : (x += outerSize) {
|
||||
|
@ -31,8 +31,8 @@ pub fn deinit() void {
|
||||
pub fn generate(worldSeed: u64, chunk: *main.chunk.Chunk, caveMap: CaveMap.CaveMapView, biomeMap: CaveBiomeMap.CaveBiomeMapView) Allocator.Error!void {
|
||||
if(chunk.pos.voxelSize < 4) {
|
||||
// Uses a blue noise pattern for all structure that shouldn't touch.
|
||||
const blueNoise = try noise.BlueNoise.getRegionData(main.threadAllocator, chunk.pos.wx - 8, chunk.pos.wz - 8, chunk.width + 16, chunk.width + 16);
|
||||
defer main.threadAllocator.free(blueNoise);
|
||||
const blueNoise = try noise.BlueNoise.getRegionData(main.stackAllocator, chunk.pos.wx - 8, chunk.pos.wz - 8, chunk.width + 16, chunk.width + 16);
|
||||
defer main.stackAllocator.free(blueNoise);
|
||||
for(blueNoise) |coordinatePair| {
|
||||
const px = @as(i32, @intCast(coordinatePair >> 16)) - 8; // TODO: Maybe add a blue-noise iterator or something like that?
|
||||
const pz = @as(i32, @intCast(coordinatePair & 0xffff)) - 8;
|
||||
|
@ -29,14 +29,14 @@ pub fn deinit() void {
|
||||
pub fn generateMapFragment(map: *ClimateMapFragment, worldSeed: u64) Allocator.Error!void {
|
||||
var seed: u64 = worldSeed;
|
||||
|
||||
const generator = try GenerationStructure.init(main.threadAllocator, map.pos.wx, map.pos.wz, ClimateMapFragment.mapSize, ClimateMapFragment.mapSize, terrain.biomes.byTypeBiomes, seed);
|
||||
defer generator.deinit(main.threadAllocator);
|
||||
const generator = try GenerationStructure.init(main.globalAllocator, map.pos.wx, map.pos.wz, ClimateMapFragment.mapSize, ClimateMapFragment.mapSize, terrain.biomes.byTypeBiomes, seed);
|
||||
defer generator.deinit(main.stackAllocator);
|
||||
|
||||
try generator.toMap(map, ClimateMapFragment.mapSize, ClimateMapFragment.mapSize, worldSeed);
|
||||
|
||||
// TODO: Remove debug image:
|
||||
const image = try main.graphics.Image.init(main.threadAllocator, @intCast(map.map.len), @intCast(map.map[0].len));
|
||||
defer image.deinit(main.threadAllocator);
|
||||
const image = try main.graphics.Image.init(main.stackAllocator, @intCast(map.map.len), @intCast(map.map[0].len));
|
||||
defer image.deinit(main.stackAllocator);
|
||||
var x: u31 = 0;
|
||||
while(x < map.map.len) : (x += 1) {
|
||||
var z: u31 = 0;
|
||||
@ -326,7 +326,7 @@ const GenerationStructure = struct {
|
||||
}
|
||||
|
||||
// Add some sub-biomes:
|
||||
var extraBiomes = std.ArrayList(BiomePoint).init(main.threadAllocator);
|
||||
var extraBiomes = std.ArrayList(BiomePoint).init(main.globalAllocator);
|
||||
defer extraBiomes.deinit();
|
||||
for(self.chunks.mem) |chunk| {
|
||||
for(chunk.biomesSortedByX) |biome| {
|
||||
|
@ -52,31 +52,31 @@ pub fn generateMapFragment(map: *MapFragment, worldSeed: u64) Allocator.Error!vo
|
||||
const mapSize = scaledSize*map.pos.voxelSize;
|
||||
const biomeSize = MapFragment.biomeSize;
|
||||
const offset = 8;
|
||||
const biomePositions = try terrain.ClimateMap.getBiomeMap(main.threadAllocator, map.pos.wx - offset*biomeSize, map.pos.wz - offset*biomeSize, mapSize + 2*offset*biomeSize, mapSize + 2*offset*biomeSize);
|
||||
defer biomePositions.deinit(main.threadAllocator);
|
||||
const biomePositions = try terrain.ClimateMap.getBiomeMap(main.stackAllocator, map.pos.wx - offset*biomeSize, map.pos.wz - offset*biomeSize, mapSize + 2*offset*biomeSize, mapSize + 2*offset*biomeSize);
|
||||
defer biomePositions.deinit(main.stackAllocator);
|
||||
var seed = random.initSeed2D(worldSeed, .{map.pos.wx, map.pos.wz});
|
||||
random.scrambleSeed(&seed);
|
||||
seed ^= seed >> 16;
|
||||
|
||||
const xOffsetMap = try Array2D(f32).init(main.threadAllocator, scaledSize, scaledSize);
|
||||
defer xOffsetMap.deinit(main.threadAllocator);
|
||||
const zOffsetMap = try Array2D(f32).init(main.threadAllocator, scaledSize, scaledSize);
|
||||
defer zOffsetMap.deinit(main.threadAllocator);
|
||||
const xOffsetMap = try Array2D(f32).init(main.stackAllocator, scaledSize, scaledSize);
|
||||
defer xOffsetMap.deinit(main.stackAllocator);
|
||||
const zOffsetMap = try Array2D(f32).init(main.stackAllocator, scaledSize, scaledSize);
|
||||
defer zOffsetMap.deinit(main.stackAllocator);
|
||||
try FractalNoise.generateSparseFractalTerrain(map.pos.wx, map.pos.wz, biomeSize*4, worldSeed ^ 675396758496549, xOffsetMap, map.pos.voxelSize);
|
||||
try FractalNoise.generateSparseFractalTerrain(map.pos.wx, map.pos.wz, biomeSize*4, worldSeed ^ 543864367373859, zOffsetMap, map.pos.voxelSize);
|
||||
|
||||
// A ridgid noise map to generate interesting mountains.
|
||||
const mountainMap = try Array2D(f32).init(main.threadAllocator, scaledSize, scaledSize);
|
||||
defer mountainMap.deinit(main.threadAllocator);
|
||||
const mountainMap = try Array2D(f32).init(main.stackAllocator, scaledSize, scaledSize);
|
||||
defer mountainMap.deinit(main.stackAllocator);
|
||||
try RandomlyWeightedFractalNoise.generateSparseFractalTerrain(map.pos.wx, map.pos.wz, 64, worldSeed ^ 6758947592930535, mountainMap, map.pos.voxelSize);
|
||||
|
||||
// A smooth map for smaller hills.
|
||||
const hillMap = try PerlinNoise.generateSmoothNoise(main.threadAllocator, map.pos.wx, map.pos.wz, mapSize, mapSize, 128, 32, worldSeed ^ 157839765839495820, map.pos.voxelSize, 0.5);
|
||||
defer hillMap.deinit(main.threadAllocator);
|
||||
const hillMap = try PerlinNoise.generateSmoothNoise(main.globalAllocator, map.pos.wx, map.pos.wz, mapSize, mapSize, 128, 32, worldSeed ^ 157839765839495820, map.pos.voxelSize, 0.5);
|
||||
defer hillMap.deinit(main.globalAllocator);
|
||||
|
||||
// A fractal map to generate high-detail roughness.
|
||||
const roughMap = try Array2D(f32).init(main.threadAllocator, scaledSize, scaledSize);
|
||||
defer roughMap.deinit(main.threadAllocator);
|
||||
const roughMap = try Array2D(f32).init(main.stackAllocator, scaledSize, scaledSize);
|
||||
defer roughMap.deinit(main.stackAllocator);
|
||||
try FractalNoise.generateSparseFractalTerrain(map.pos.wx, map.pos.wz, 64, worldSeed ^ 954936678493, roughMap, map.pos.voxelSize);
|
||||
|
||||
var x: u31 = 0;
|
||||
|
@ -10,8 +10,8 @@ fn setSeed(x: i32, z: i32, offsetX: i32, offsetZ: i32, seed: *u64, worldSeed: u6
|
||||
pub fn generateFractalTerrain(wx: i32, wz: i32, x0: u31, z0: u31, width: u32, height: u32, scale: u31, worldSeed: u64, map: Array2D(f32), maxResolution: u31) !void {
|
||||
const max = scale + 1;
|
||||
const mask: i32 = scale - 1;
|
||||
const bigMap = try Array2D(f32).init(main.threadAllocator, max, max);
|
||||
defer bigMap.deinit(main.threadAllocator);
|
||||
const bigMap = try Array2D(f32).init(main.stackAllocator, max, max);
|
||||
defer bigMap.deinit(main.stackAllocator);
|
||||
const offsetX = wx & ~mask;
|
||||
const offsetZ = wz & ~mask;
|
||||
var seed: u64 = undefined;
|
||||
|
@ -119,10 +119,10 @@ const Context = struct {
|
||||
}
|
||||
|
||||
fn freeGridPoints(self: *Context, allocator: Allocator) void {
|
||||
self.xGridPoints.deinit(allocator);
|
||||
self.yGridPoints.deinit(allocator);
|
||||
self.xGridPoints = undefined;
|
||||
self.xGridPoints.deinit(allocator);
|
||||
self.yGridPoints = undefined;
|
||||
self.xGridPoints = undefined;
|
||||
}
|
||||
};
|
||||
|
||||
@ -143,8 +143,8 @@ pub fn generateRidgidNoise(allocator: Allocator, x: i32, y: i32, width: u31, hei
|
||||
context.resolutionMask = scale - 1;
|
||||
const x0 = x & ~context.resolutionMask;
|
||||
const y0 = y & ~context.resolutionMask;
|
||||
try context.calculateGridPoints(main.threadAllocator, x, y, width, height, scale);
|
||||
defer context.freeGridPoints(main.threadAllocator);
|
||||
try context.calculateGridPoints(main.globalAllocator, x, y, width, height, scale);
|
||||
defer context.freeGridPoints(main.globalAllocator);
|
||||
|
||||
var x1 = x;
|
||||
while(x1 -% width -% x < 0) : (x1 += voxelSize) {
|
||||
@ -175,8 +175,8 @@ pub fn generateSmoothNoise(allocator: Allocator, x: i32, y: i32, width: u31, hei
|
||||
context.resolutionMask = scale - 1;
|
||||
const x0 = x & ~context.resolutionMask;
|
||||
const y0 = y & ~context.resolutionMask;
|
||||
try context.calculateGridPoints(main.threadAllocator, x, y, width, height, scale);
|
||||
defer context.freeGridPoints(main.threadAllocator);
|
||||
try context.calculateGridPoints(main.stackAllocator, x, y, width, height, scale);
|
||||
defer context.freeGridPoints(main.stackAllocator);
|
||||
|
||||
var x1 = x;
|
||||
while(x1 -% width -% x < 0) : (x1 += voxelSize) {
|
||||
|
@ -10,8 +10,8 @@ fn setSeed(x: i32, z: i32, offsetX: i32, offsetZ: i32, seed: *u64, worldSeed: u6
|
||||
pub fn generateFractalTerrain(wx: i32, wz: i32, x0: u31, z0: u31, width: u32, height: u32, scale: u31, worldSeed: u64, map: Array2D(f32), maxResolution: u31) !void {
|
||||
const max = scale + 1;
|
||||
const mask: i32 = scale - 1;
|
||||
const bigMap = try Array2D(f32).init(main.threadAllocator, max, max);
|
||||
defer bigMap.deinit(main.threadAllocator);
|
||||
const bigMap = try Array2D(f32).init(main.stackAllocator, max, max);
|
||||
defer bigMap.deinit(main.stackAllocator);
|
||||
const offsetX = wx & ~mask;
|
||||
const offsetZ = wz & ~mask;
|
||||
var seed: u64 = undefined;
|
||||
|
@ -211,8 +211,8 @@ const WorldIO = struct {
|
||||
|
||||
/// Load the seed, which is needed before custom item and ore generation.
|
||||
pub fn loadWorldSeed(self: WorldIO) !u64 {
|
||||
const worldData: JsonElement = try self.dir.readToJson(main.threadAllocator, "world.dat");
|
||||
defer worldData.free(main.threadAllocator);
|
||||
const worldData: JsonElement = try self.dir.readToJson(main.globalAllocator, "world.dat");
|
||||
defer worldData.free(main.globalAllocator);
|
||||
if(worldData.get(u32, "version", 0) != worldDataVersion) {
|
||||
std.log.err("Cannot read world file version {}. Expected version {}.", .{worldData.get(u32, "version", 0), worldDataVersion});
|
||||
return error.OldWorld;
|
||||
@ -221,8 +221,8 @@ const WorldIO = struct {
|
||||
}
|
||||
|
||||
pub fn loadWorldData(self: WorldIO) !void {
|
||||
const worldData: JsonElement = try self.dir.readToJson(main.threadAllocator, "world.dat");
|
||||
defer worldData.free(main.threadAllocator);
|
||||
const worldData: JsonElement = try self.dir.readToJson(main.globalAllocator, "world.dat");
|
||||
defer worldData.free(main.globalAllocator);
|
||||
|
||||
const entityJson = worldData.getChild("entities");
|
||||
_ = entityJson;
|
||||
@ -242,15 +242,15 @@ const WorldIO = struct {
|
||||
}
|
||||
|
||||
pub fn saveWorldData(self: WorldIO) !void {
|
||||
const worldData: JsonElement = try JsonElement.initObject(main.threadAllocator);
|
||||
defer worldData.free(main.threadAllocator);
|
||||
const worldData: JsonElement = try JsonElement.initObject(main.globalAllocator);
|
||||
defer worldData.free(main.globalAllocator);
|
||||
try worldData.put("version", worldDataVersion);
|
||||
try worldData.put("seed", self.world.seed);
|
||||
try worldData.put("doGameTimeCycle", self.world.doGameTimeCycle);
|
||||
try worldData.put("gameTime", self.world.gameTime);
|
||||
// TODO:
|
||||
// worldData.put("entityCount", world.getEntities().length);
|
||||
const spawnData = try JsonElement.initObject(main.threadAllocator);
|
||||
const spawnData = try JsonElement.initObject(main.globalAllocator);
|
||||
try spawnData.put("x", self.world.spawn[0]);
|
||||
try spawnData.put("y", self.world.spawn[1]);
|
||||
try spawnData.put("z", self.world.spawn[2]);
|
||||
@ -306,7 +306,7 @@ pub const ServerWorld = struct {
|
||||
};
|
||||
try self.itemDropManager.init(main.globalAllocator, self, self.gravity);
|
||||
|
||||
var loadArena = std.heap.ArenaAllocator.init(main.threadAllocator);
|
||||
var loadArena = std.heap.ArenaAllocator.init(main.globalAllocator);
|
||||
defer loadArena.deinit();
|
||||
const arenaAllocator = loadArena.allocator();
|
||||
var buf: [32768]u8 = undefined;
|
||||
@ -374,8 +374,8 @@ pub const ServerWorld = struct {
|
||||
|
||||
pub fn findPlayer(self: *ServerWorld, user: *User) !void {
|
||||
var buf: [1024]u8 = undefined;
|
||||
const playerData = files.readToJson(main.threadAllocator, try std.fmt.bufPrint(&buf, "saves/{s}/player/{s}.json", .{self.name, user.name})) catch .JsonNull; // TODO: Utils.escapeFolderName(user.name)
|
||||
defer playerData.free(main.threadAllocator);
|
||||
const playerData = files.readToJson(main.globalAllocator, try std.fmt.bufPrint(&buf, "saves/{s}/player/{s}.json", .{self.name, user.name})) catch .JsonNull; // TODO: Utils.escapeFolderName(user.name)
|
||||
defer playerData.free(main.globalAllocator);
|
||||
const player = &user.player;
|
||||
if(playerData == .JsonNull) {
|
||||
// Generate a new player:
|
||||
@ -409,8 +409,8 @@ pub const ServerWorld = struct {
|
||||
// savePlayers();
|
||||
// chunkManager.forceSave();
|
||||
// ChunkIO.save();
|
||||
const itemDropJson = self.itemDropManager.store(main.threadAllocator);
|
||||
defer itemDropJson.free(main.threadAllocator);
|
||||
const itemDropJson = self.itemDropManager.store(main.globalAllocator);
|
||||
defer itemDropJson.free(main.globalAllocator);
|
||||
var buf: [32768]u8 = undefined;
|
||||
files.writeJson(try std.fmt.bufPrint(&buf, "saves/{s}/items.json", .{self.name}), itemDropJson);
|
||||
}
|
||||
|
@ -39,11 +39,11 @@ pub var developerAutoEnterWorld: []const u8 = "";
|
||||
|
||||
|
||||
pub fn init() !void {
|
||||
const json: JsonElement = main.files.readToJson(main.threadAllocator, "settings.json") catch |err| blk: {
|
||||
const json: JsonElement = main.files.readToJson(main.globalAllocator, "settings.json") catch |err| blk: {
|
||||
if(err == error.FileNotFound) break :blk JsonElement{.JsonNull={}};
|
||||
return err;
|
||||
};
|
||||
defer json.free(main.threadAllocator);
|
||||
defer json.free(main.globalAllocator);
|
||||
|
||||
inline for(@typeInfo(@This()).Struct.decls) |decl| {
|
||||
const is_const = @typeInfo(@TypeOf(&@field(@This(), decl.name))).Pointer.is_const; // Sadly there is no direct way to check if a declaration is const.
|
||||
@ -74,8 +74,8 @@ pub fn init() !void {
|
||||
}
|
||||
|
||||
fn flawedDeinit() !void {
|
||||
const jsonObject = try JsonElement.initObject(main.threadAllocator);
|
||||
defer jsonObject.free(main.threadAllocator);
|
||||
const jsonObject = try JsonElement.initObject(main.globalAllocator);
|
||||
defer jsonObject.free(main.globalAllocator);
|
||||
|
||||
inline for(@typeInfo(@This()).Struct.decls) |decl| {
|
||||
const is_const = @typeInfo(@TypeOf(&@field(@This(), decl.name))).Pointer.is_const; // Sadly there is no direct way to check if a declaration is const.
|
||||
@ -100,9 +100,9 @@ fn flawedDeinit() !void {
|
||||
}
|
||||
|
||||
// keyboard settings:
|
||||
const keyboard = try JsonElement.initObject(main.threadAllocator);
|
||||
const keyboard = try JsonElement.initObject(main.globalAllocator);
|
||||
for(&main.KeyBoard.keys) |key| {
|
||||
const keyJson = try JsonElement.initObject(main.threadAllocator);
|
||||
const keyJson = try JsonElement.initObject(main.globalAllocator);
|
||||
try keyJson.put("key", key.key);
|
||||
try keyJson.put("mouseButton", key.mouseButton);
|
||||
try keyJson.put("scancode", key.scancode);
|
||||
|
162
src/utils.zig
162
src/utils.zig
@ -8,7 +8,7 @@ const main = @import("main.zig");
|
||||
pub const Compression = struct {
|
||||
pub fn deflate(allocator: Allocator, data: []const u8) ![]u8 {
|
||||
var result = std.ArrayList(u8).init(allocator);
|
||||
var comp = try std.compress.deflate.compressor(main.threadAllocator, result.writer(), .{.level = .default_compression});
|
||||
var comp = try std.compress.deflate.compressor(main.globalAllocator, result.writer(), .{.level = .default_compression});
|
||||
_ = try comp.write(data);
|
||||
try comp.close();
|
||||
comp.deinit();
|
||||
@ -17,27 +17,27 @@ pub const Compression = struct {
|
||||
|
||||
pub fn inflateTo(buf: []u8, data: []const u8) !usize {
|
||||
var stream = std.io.fixedBufferStream(data);
|
||||
var decomp = try std.compress.deflate.decompressor(main.threadAllocator, stream.reader(), null);
|
||||
var decomp = try std.compress.deflate.decompressor(main.globalAllocator, stream.reader(), null);
|
||||
defer decomp.deinit();
|
||||
return try decomp.reader().readAll(buf);
|
||||
}
|
||||
|
||||
pub fn pack(sourceDir: std.fs.IterableDir, writer: anytype) !void {
|
||||
var comp = try std.compress.deflate.compressor(main.threadAllocator, writer, .{.level = .default_compression});
|
||||
var comp = try std.compress.deflate.compressor(main.globalAllocator, writer, .{.level = .default_compression});
|
||||
defer comp.deinit();
|
||||
var walker = try sourceDir.walk(main.threadAllocator);
|
||||
var walker = try sourceDir.walk(main.globalAllocator);
|
||||
defer walker.deinit();
|
||||
|
||||
while(try walker.next()) |entry| {
|
||||
if(entry.kind == .file) {
|
||||
var relPath = entry.path;
|
||||
if(builtin.os.tag == .windows) { // I hate you
|
||||
const copy = try main.threadAllocator.dupe(u8, relPath);
|
||||
const copy = try main.stackAllocator.dupe(u8, relPath);
|
||||
std.mem.replaceScalar(u8, copy, '\\', '/');
|
||||
relPath = copy;
|
||||
}
|
||||
defer if(builtin.os.tag == .windows) {
|
||||
main.threadAllocator.free(relPath);
|
||||
main.stackAllocator.free(relPath);
|
||||
};
|
||||
var len: [4]u8 = undefined;
|
||||
std.mem.writeInt(u32, &len, @as(u32, @intCast(relPath.len)), .big);
|
||||
@ -46,8 +46,8 @@ pub const Compression = struct {
|
||||
|
||||
const file = try sourceDir.dir.openFile(relPath, .{});
|
||||
defer file.close();
|
||||
const fileData = try file.readToEndAlloc(main.threadAllocator, std.math.maxInt(u32));
|
||||
defer main.threadAllocator.free(fileData);
|
||||
const fileData = try file.readToEndAlloc(main.stackAllocator, std.math.maxInt(u32));
|
||||
defer main.stackAllocator.free(fileData);
|
||||
|
||||
std.mem.writeInt(u32, &len, @as(u32, @intCast(fileData.len)), .big);
|
||||
_ = try comp.write(&len);
|
||||
@ -59,11 +59,11 @@ pub const Compression = struct {
|
||||
|
||||
pub fn unpack(outDir: std.fs.Dir, input: []const u8) !void {
|
||||
var stream = std.io.fixedBufferStream(input);
|
||||
var decomp = try std.compress.deflate.decompressor(main.threadAllocator, stream.reader(), null);
|
||||
var decomp = try std.compress.deflate.decompressor(main.globalAllocator, stream.reader(), null);
|
||||
defer decomp.deinit();
|
||||
const reader = decomp.reader();
|
||||
const _data = try reader.readAllAlloc(main.threadAllocator, std.math.maxInt(usize));
|
||||
defer main.threadAllocator.free(_data);
|
||||
const _data = try reader.readAllAlloc(main.stackAllocator, std.math.maxInt(usize));
|
||||
defer main.stackAllocator.free(_data);
|
||||
var data = _data;
|
||||
while(data.len != 0) {
|
||||
var len = std.mem.readInt(u32, data[0..4], .big);
|
||||
@ -132,8 +132,8 @@ pub fn AliasTable(comptime T: type) type {
|
||||
};
|
||||
if(items.len == 0) return self;
|
||||
@memset(self.aliasData, AliasData{.chance = 0, .alias = 0});
|
||||
const currentChances = try main.threadAllocator.alloc(f32, items.len);
|
||||
defer main.threadAllocator.free(currentChances);
|
||||
const currentChances = try main.stackAllocator.alloc(f32, items.len);
|
||||
defer main.stackAllocator.free(currentChances);
|
||||
var totalChance: f32 = 0;
|
||||
for(items, 0..) |*item, i| {
|
||||
totalChance += item.chance;
|
||||
@ -157,8 +157,8 @@ pub fn AliasTable(comptime T: type) type {
|
||||
};
|
||||
if(items.len == 0) return self;
|
||||
@memset(self.aliasData, AliasData{.chance = 0, .alias = 0});
|
||||
const currentChances = try main.threadAllocator.alloc(f32, items.len);
|
||||
defer main.threadAllocator.free(currentChances);
|
||||
const currentChances = try main.stackAllocator.alloc(f32, items.len);
|
||||
defer main.stackAllocator.free(currentChances);
|
||||
var totalChance: f32 = 0;
|
||||
for(slice, 0..) |context, i| {
|
||||
totalChance += context.chance;
|
||||
@ -315,6 +315,130 @@ pub fn Array3D(comptime T: type) type {
|
||||
};
|
||||
}
|
||||
|
||||
/// Allows for stack-like allocations in a fast and safe way.
|
||||
/// It is safe in the sense that a regular allocator will be used when the buffer is full.
|
||||
pub const StackAllocator = struct {
|
||||
const Allocation = struct{start: u32, len: u32};
|
||||
backingAllocator: Allocator,
|
||||
buffer: []align(4096) u8,
|
||||
allocationList: std.ArrayList(Allocation),
|
||||
index: usize,
|
||||
|
||||
pub fn init(backingAllocator: Allocator, size: u32) !StackAllocator {
|
||||
return .{
|
||||
.backingAllocator = backingAllocator,
|
||||
.buffer = try backingAllocator.alignedAlloc(u8, 4096, size),
|
||||
.allocationList = std.ArrayList(Allocation).init(backingAllocator),
|
||||
.index = 0,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: StackAllocator) void {
|
||||
if(self.allocationList.items.len != 0) {
|
||||
std.log.err("Memory leak in Stack Allocator", .{});
|
||||
}
|
||||
self.allocationList.deinit();
|
||||
self.backingAllocator.free(self.buffer);
|
||||
}
|
||||
|
||||
pub fn allocator(self: *StackAllocator) Allocator {
|
||||
return .{
|
||||
.vtable = &.{
|
||||
.alloc = &alloc,
|
||||
.resize = &resize,
|
||||
.free = &free,
|
||||
},
|
||||
.ptr = self,
|
||||
};
|
||||
}
|
||||
|
||||
fn isInsideBuffer(self: *StackAllocator, buf: []u8) bool {
|
||||
const bufferStart = @intFromPtr(self.buffer.ptr);
|
||||
const bufferEnd = bufferStart + self.buffer.len;
|
||||
const compare = @intFromPtr(buf.ptr);
|
||||
return compare >= bufferStart and compare < bufferEnd;
|
||||
}
|
||||
|
||||
fn indexInBuffer(self: *StackAllocator, buf: []u8) usize {
|
||||
const bufferStart = @intFromPtr(self.buffer.ptr);
|
||||
const compare = @intFromPtr(buf.ptr);
|
||||
return compare - bufferStart;
|
||||
}
|
||||
|
||||
/// Attempt to allocate exactly `len` bytes aligned to `1 << ptr_align`.
|
||||
///
|
||||
/// `ret_addr` is optionally provided as the first return address of the
|
||||
/// allocation call stack. If the value is `0` it means no return address
|
||||
/// has been provided.
|
||||
fn alloc(ctx: *anyopaque, len: usize, ptr_align: u8, ret_addr: usize) ?[*]u8 {
|
||||
const self: *StackAllocator = @ptrCast(@alignCast(ctx));
|
||||
if(len >= self.buffer.len) return self.backingAllocator.rawAlloc(len, ptr_align, ret_addr);
|
||||
var start = std.mem.alignForward(usize, self.index, @as(usize, 1) << @intCast(ptr_align));
|
||||
if(start + len >= self.buffer.len) return self.backingAllocator.rawAlloc(len, ptr_align, ret_addr);
|
||||
self.allocationList.append(.{.start = @intCast(start), .len = @intCast(len)}) catch return null;
|
||||
self.index = start + len;
|
||||
return self.buffer.ptr + start;
|
||||
}
|
||||
|
||||
/// Attempt to expand or shrink memory in place. `buf.len` must equal the
|
||||
/// length requested from the most recent successful call to `alloc` or
|
||||
/// `resize`. `buf_align` must equal the same value that was passed as the
|
||||
/// `ptr_align` parameter to the original `alloc` call.
|
||||
///
|
||||
/// A result of `true` indicates the resize was successful and the
|
||||
/// allocation now has the same address but a size of `new_len`. `false`
|
||||
/// indicates the resize could not be completed without moving the
|
||||
/// allocation to a different address.
|
||||
///
|
||||
/// `new_len` must be greater than zero.
|
||||
///
|
||||
/// `ret_addr` is optionally provided as the first return address of the
|
||||
/// allocation call stack. If the value is `0` it means no return address
|
||||
/// has been provided.
|
||||
fn resize(ctx: *anyopaque, buf: []u8, buf_align: u8, new_len: usize, ret_addr: usize) bool {
|
||||
const self: *StackAllocator = @ptrCast(@alignCast(ctx));
|
||||
if(self.isInsideBuffer(buf)) {
|
||||
const top = &self.allocationList.items[self.allocationList.items.len - 1];
|
||||
std.debug.assert(top.start == self.indexInBuffer(buf)); // Can only resize the top element.
|
||||
std.debug.assert(top.len == buf.len);
|
||||
std.debug.assert(self.index >= top.start + top.len);
|
||||
if(top.start + new_len >= self.buffer.len) {
|
||||
return false;
|
||||
}
|
||||
self.index -= top.len;
|
||||
self.index += new_len;
|
||||
top.len = @intCast(new_len);
|
||||
return true;
|
||||
} else {
|
||||
return self.backingAllocator.rawResize(buf, buf_align, new_len, ret_addr);
|
||||
}
|
||||
}
|
||||
|
||||
/// Free and invalidate a buffer.
|
||||
///
|
||||
/// `buf.len` must equal the most recent length returned by `alloc` or
|
||||
/// given to a successful `resize` call.
|
||||
///
|
||||
/// `buf_align` must equal the same value that was passed as the
|
||||
/// `ptr_align` parameter to the original `alloc` call.
|
||||
///
|
||||
/// `ret_addr` is optionally provided as the first return address of the
|
||||
/// allocation call stack. If the value is `0` it means no return address
|
||||
/// has been provided.
|
||||
fn free(ctx: *anyopaque, buf: []u8, buf_align: u8, ret_addr: usize) void {
|
||||
const self: *StackAllocator = @ptrCast(@alignCast(ctx));
|
||||
if(self.isInsideBuffer(buf)) {
|
||||
const top = self.allocationList.pop();
|
||||
std.debug.assert(top.start == self.indexInBuffer(buf)); // Can only free the top element.
|
||||
std.debug.assert(top.len == buf.len);
|
||||
std.debug.assert(self.index >= top.start + top.len);
|
||||
self.index = top.start;
|
||||
} else {
|
||||
self.backingAllocator.rawFree(buf, buf_align, ret_addr);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/// A simple binary heap.
|
||||
/// Thread safe and blocking.
|
||||
/// Expects T to have a `biggerThan(T) bool` function
|
||||
@ -530,11 +654,9 @@ pub const ThreadPool = struct {
|
||||
|
||||
fn run(self: ThreadPool, id: usize) !void {
|
||||
// In case any of the tasks wants to allocate memory:
|
||||
var gpa = std.heap.GeneralPurposeAllocator(.{.thread_safe=false}){};
|
||||
main.threadAllocator = gpa.allocator();
|
||||
defer if(gpa.deinit() == .leak) {
|
||||
std.log.err("Memory leak", .{});
|
||||
};
|
||||
var sta = try StackAllocator.init(main.globalAllocator, 1 << 23);
|
||||
defer sta.deinit();
|
||||
main.stackAllocator = sta.allocator();
|
||||
|
||||
var lastUpdate = std.time.milliTimestamp();
|
||||
while(true) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user