Handle errors locally instead of letting them bubble up into the main functions.

This commit is contained in:
IntegratedQuantum 2024-01-31 22:42:20 +01:00
parent 05769cc765
commit 74c896c969
5 changed files with 107 additions and 54 deletions

View File

@ -15,18 +15,23 @@ var commonItems: std.StringHashMap(JsonElement) = undefined;
var commonRecipes: main.List([]const u8) = undefined; var commonRecipes: main.List([]const u8) = undefined;
/// Reads json files recursively from all subfolders. /// Reads json files recursively from all subfolders.
pub fn readAllJsonFilesInAddons(externalAllocator: NeverFailingAllocator, addons: main.List(std.fs.Dir), addonNames: main.List([]const u8), subPath: []const u8, output: *std.StringHashMap(JsonElement)) !void { pub fn readAllJsonFilesInAddons(externalAllocator: NeverFailingAllocator, addons: main.List(std.fs.Dir), addonNames: main.List([]const u8), subPath: []const u8, output: *std.StringHashMap(JsonElement)) void {
for(addons.items, addonNames.items) |addon, addonName| { for(addons.items, addonNames.items) |addon, addonName| {
var dir = addon.openDir(subPath, .{.iterate = true}) catch |err| { var dir = addon.openDir(subPath, .{.iterate = true}) catch |err| {
if(err == error.FileNotFound) continue; if(err != error.FileNotFound) {
return err; std.log.err("Could not open addon directory {s}: {s}", .{subPath, @errorName(err)});
}
continue;
}; };
defer dir.close(); defer dir.close();
var walker = try dir.walk(main.globalAllocator.allocator); var walker = dir.walk(main.globalAllocator.allocator) catch unreachable;
defer walker.deinit(); defer walker.deinit();
while(try walker.next()) |entry| { while(walker.next() catch |err| blk: {
std.log.err("Got error while iterating addon directory {s}: {s}", .{subPath, @errorName(err)});
break :blk null;
}) |entry| {
if(entry.kind == .file and std.ascii.endsWithIgnoreCase(entry.basename, ".json")) { if(entry.kind == .file and std.ascii.endsWithIgnoreCase(entry.basename, ".json")) {
const folderName = addonName; const folderName = addonName;
const id: []u8 = externalAllocator.alloc(u8, folderName.len + 1 + entry.path.len - 5); const id: []u8 = externalAllocator.alloc(u8, folderName.len + 1 + entry.path.len - 5);
@ -41,30 +46,41 @@ pub fn readAllJsonFilesInAddons(externalAllocator: NeverFailingAllocator, addons
} }
} }
const file = try dir.openFile(entry.path, .{}); const file = dir.openFile(entry.path, .{}) catch |err| {
std.log.err("Could not open {s}/{s}: {s}", .{subPath, entry.path, @errorName(err)});
continue;
};
defer file.close(); defer file.close();
const string = file.readToEndAlloc(main.stackAllocator.allocator, std.math.maxInt(usize)) catch unreachable; const string = file.readToEndAlloc(main.stackAllocator.allocator, std.math.maxInt(usize)) catch unreachable;
defer main.stackAllocator.free(string); defer main.stackAllocator.free(string);
try output.put(id, JsonElement.parseFromString(externalAllocator, string)); output.put(id, JsonElement.parseFromString(externalAllocator, string)) catch unreachable;
} }
} }
} }
} }
/// Reads text files recursively from all subfolders. /// Reads text files recursively from all subfolders.
pub fn readAllFilesInAddons(externalAllocator: NeverFailingAllocator, addons: main.List(std.fs.Dir), subPath: []const u8, output: *main.List([]const u8)) !void { pub fn readAllFilesInAddons(externalAllocator: NeverFailingAllocator, addons: main.List(std.fs.Dir), subPath: []const u8, output: *main.List([]const u8)) void {
for(addons.items) |addon| { for(addons.items) |addon| {
var dir = addon.openDir(subPath, .{.iterate = true}) catch |err| { var dir = addon.openDir(subPath, .{.iterate = true}) catch |err| {
if(err == error.FileNotFound) continue; if(err != error.FileNotFound) {
return err; std.log.err("Could not open addon directory {s}: {s}", .{subPath, @errorName(err)});
}
continue;
}; };
defer dir.close(); defer dir.close();
var walker = try dir.walk(main.globalAllocator.allocator); var walker = dir.walk(main.globalAllocator.allocator) catch unreachable;
defer walker.deinit(); defer walker.deinit();
while(try walker.next()) |entry| { while(walker.next() catch |err| blk: {
std.log.err("Got error while iterating addon directory {s}: {s}", .{subPath, @errorName(err)});
break :blk null;
}) |entry| {
if(entry.kind == .file) { if(entry.kind == .file) {
const file = try dir.openFile(entry.path, .{}); const file = dir.openFile(entry.path, .{}) catch |err| {
std.log.err("Could not open {s}/{s}: {s}", .{subPath, entry.path, @errorName(err)});
continue;
};
defer file.close(); defer file.close();
const string = file.readToEndAlloc(externalAllocator.allocator, std.math.maxInt(usize)) catch unreachable; const string = file.readToEndAlloc(externalAllocator.allocator, std.math.maxInt(usize)) catch unreachable;
output.append(string); output.append(string);
@ -73,19 +89,28 @@ pub fn readAllFilesInAddons(externalAllocator: NeverFailingAllocator, addons: ma
} }
} }
pub fn readAssets(externalAllocator: NeverFailingAllocator, assetPath: []const u8, blocks: *std.StringHashMap(JsonElement), items: *std.StringHashMap(JsonElement), biomes: *std.StringHashMap(JsonElement), recipes: *main.List([]const u8)) !void { pub fn readAssets(externalAllocator: NeverFailingAllocator, assetPath: []const u8, blocks: *std.StringHashMap(JsonElement), items: *std.StringHashMap(JsonElement), biomes: *std.StringHashMap(JsonElement), recipes: *main.List([]const u8)) void {
var addons = main.List(std.fs.Dir).init(main.globalAllocator); var addons = main.List(std.fs.Dir).init(main.globalAllocator);
defer addons.deinit(); defer addons.deinit();
var addonNames = main.List([]const u8).init(main.globalAllocator); var addonNames = main.List([]const u8).init(main.globalAllocator);
defer addonNames.deinit(); defer addonNames.deinit();
{ // Find all the sub-directories to the assets folder. { // Find all the sub-directories to the assets folder.
var dir = try std.fs.cwd().openDir(assetPath, .{.iterate = true}); var dir = std.fs.cwd().openDir(assetPath, .{.iterate = true}) catch |err| {
std.log.err("Can't open asset path {s}: {s}", .{assetPath, @errorName(err)});
return;
};
defer dir.close(); defer dir.close();
var iterator = dir.iterate(); var iterator = dir.iterate();
while(try iterator.next()) |addon| { while(iterator.next() catch |err| blk: {
std.log.err("Got error while iterating over asset path {s}: {s}", .{assetPath, @errorName(err)});
break :blk null;
}) |addon| {
if(addon.kind == .directory) { if(addon.kind == .directory) {
addons.append(try dir.openDir(addon.name, .{})); addons.append(dir.openDir(addon.name, .{}) catch |err| {
std.log.err("Got error while reading addon {s} from {s}: {s}", .{addon.name, assetPath, @errorName(err)});
continue;
});
addonNames.append(main.globalAllocator.dupe(u8, addon.name)); addonNames.append(main.globalAllocator.dupe(u8, addon.name));
} }
} }
@ -95,13 +120,13 @@ pub fn readAssets(externalAllocator: NeverFailingAllocator, assetPath: []const u
main.globalAllocator.free(addonName); main.globalAllocator.free(addonName);
}; };
try readAllJsonFilesInAddons(externalAllocator, addons, addonNames, "blocks", blocks); readAllJsonFilesInAddons(externalAllocator, addons, addonNames, "blocks", blocks);
try readAllJsonFilesInAddons(externalAllocator, addons, addonNames, "items", items); readAllJsonFilesInAddons(externalAllocator, addons, addonNames, "items", items);
try readAllJsonFilesInAddons(externalAllocator, addons, addonNames, "biomes", biomes); readAllJsonFilesInAddons(externalAllocator, addons, addonNames, "biomes", biomes);
try readAllFilesInAddons(externalAllocator, addons, "recipes", recipes); readAllFilesInAddons(externalAllocator, addons, "recipes", recipes);
} }
pub fn init() !void { pub fn init() void {
biomes_zig.init(); biomes_zig.init();
blocks_zig.init(); blocks_zig.init();
arena = main.utils.NeverFailingArenaAllocator.init(main.globalAllocator); arena = main.utils.NeverFailingArenaAllocator.init(main.globalAllocator);
@ -111,7 +136,7 @@ pub fn init() !void {
commonBiomes = std.StringHashMap(JsonElement).init(arenaAllocator.allocator); commonBiomes = std.StringHashMap(JsonElement).init(arenaAllocator.allocator);
commonRecipes = main.List([]const u8).init(arenaAllocator); commonRecipes = main.List([]const u8).init(arenaAllocator);
try readAssets(arenaAllocator, "assets/", &commonBlocks, &commonItems, &commonBiomes, &commonRecipes); readAssets(arenaAllocator, "assets/", &commonBlocks, &commonItems, &commonBiomes, &commonRecipes);
} }
fn registerItem(assetFolder: []const u8, id: []const u8, json: JsonElement) !*items_zig.BaseItem { fn registerItem(assetFolder: []const u8, id: []const u8, json: JsonElement) !*items_zig.BaseItem {
@ -209,7 +234,7 @@ pub fn loadWorldAssets(assetFolder: []const u8, palette: *BlockPalette) !void {
recipes.appendSlice(commonRecipes.items); recipes.appendSlice(commonRecipes.items);
defer recipes.clearAndFree(); defer recipes.clearAndFree();
try readAssets(arenaAllocator, assetFolder, &blocks, &items, &biomes, &recipes); readAssets(arenaAllocator, assetFolder, &blocks, &items, &biomes, &recipes);
// blocks: // blocks:
var block: u32 = 0; var block: u32 = 0;

View File

@ -1048,7 +1048,7 @@ const TextRendering = struct {
} }
}; };
pub fn init() !void { pub fn init() void {
draw.initCircle(); draw.initCircle();
draw.initDrawRect(); draw.initDrawRect();
draw.initImage(); draw.initImage();

View File

@ -124,7 +124,7 @@ pub const Callback = struct {
} }
}; };
pub fn init() !void { pub fn init() void {
GuiCommandQueue.init(); GuiCommandQueue.init();
windowList = List(*GuiWindow).init(main.globalAllocator); windowList = List(*GuiWindow).init(main.globalAllocator);
hudWindows = List(*GuiWindow).init(main.globalAllocator); hudWindows = List(*GuiWindow).init(main.globalAllocator);

View File

@ -48,7 +48,7 @@ fn cacheStringImpl(comptime len: usize, comptime str: [len]u8) []const u8 {
fn cacheString(comptime str: []const u8) []const u8 { fn cacheString(comptime str: []const u8) []const u8 {
return cacheStringImpl(str.len, str[0..].*); return cacheStringImpl(str.len, str[0..].*);
} }
var logFile: std.fs.File = undefined; var logFile: ?std.fs.File = undefined;
var supportsANSIColors: bool = undefined; var supportsANSIColors: bool = undefined;
// overwrite the log function: // overwrite the log function:
pub const std_options = struct { pub const std_options = struct {
@ -181,6 +181,26 @@ pub const std_options = struct {
} }
}; };
fn initLogging() void {
logFile = null;
std.fs.cwd().makePath("logs") catch |err| {
std.log.err("Couldn't create logs folder: {s}", .{@errorName(err)});
return;
};
logFile = std.fs.cwd().createFile("logs/latest.log", .{}) catch |err| {
std.log.err("Couldn't create logs/latest.log: {s}", .{@errorName(err)});
return;
};
supportsANSIColors = std.io.getStdOut().supportsAnsiEscapeCodes();
}
fn deinitLogging() void {
if(logFile) |_logFile| {
_logFile.close();
logFile = null;
}
}
fn logToFile(comptime format: []const u8, args: anytype) void { fn logToFile(comptime format: []const u8, args: anytype) void {
var buf: [65536]u8 = undefined; var buf: [65536]u8 = undefined;
var fba = std.heap.FixedBufferAllocator.init(&buf); var fba = std.heap.FixedBufferAllocator.init(&buf);
@ -188,7 +208,7 @@ fn logToFile(comptime format: []const u8, args: anytype) void {
const string = std.fmt.allocPrint(allocator, format, args) catch format; const string = std.fmt.allocPrint(allocator, format, args) catch format;
defer allocator.free(string); defer allocator.free(string);
logFile.writeAll(string) catch {}; (logFile orelse return).writeAll(string) catch {};
} }
fn logToStdErr(comptime format: []const u8, args: anytype) void { fn logToStdErr(comptime format: []const u8, args: anytype) void {
@ -587,11 +607,11 @@ pub const Window = struct {
c.glfwSetClipboardString(window, nullTerminatedString.ptr); c.glfwSetClipboardString(window, nullTerminatedString.ptr);
} }
fn init() !void { fn init() void {
_ = c.glfwSetErrorCallback(GLFWCallbacks.errorCallback); _ = c.glfwSetErrorCallback(GLFWCallbacks.errorCallback);
if(c.glfwInit() == 0) { if(c.glfwInit() == 0) {
return error.GLFWFailed; @panic("Failed to initialize GLFW");
} }
if(@import("builtin").mode == .Debug) { if(@import("builtin").mode == .Debug) {
@ -600,7 +620,7 @@ pub const Window = struct {
c.glfwWindowHint(c.GLFW_CONTEXT_VERSION_MAJOR, 4); c.glfwWindowHint(c.GLFW_CONTEXT_VERSION_MAJOR, 4);
c.glfwWindowHint(c.GLFW_CONTEXT_VERSION_MINOR, 6); c.glfwWindowHint(c.GLFW_CONTEXT_VERSION_MINOR, 6);
window = c.glfwCreateWindow(width, height, "Cubyz", null, null) orelse return error.GLFWFailed; window = c.glfwCreateWindow(width, height, "Cubyz", null, null) orelse @panic("Failed to create GLFW window");
_ = c.glfwSetKeyCallback(window, GLFWCallbacks.keyCallback); _ = c.glfwSetKeyCallback(window, GLFWCallbacks.keyCallback);
_ = c.glfwSetCharCallback(window, GLFWCallbacks.charCallback); _ = c.glfwSetCharCallback(window, GLFWCallbacks.charCallback);
@ -612,7 +632,7 @@ pub const Window = struct {
c.glfwMakeContextCurrent(window); c.glfwMakeContextCurrent(window);
if(c.gladLoadGL() == 0) { if(c.gladLoadGL() == 0) {
return error.GLADFailed; @panic("Failed to load OpenGL functions from GLAD");
} }
reloadSettings(); reloadSettings();
@ -657,7 +677,7 @@ pub const Window = struct {
pub var lastFrameTime = std.atomic.Value(f64).init(0); pub var lastFrameTime = std.atomic.Value(f64).init(0);
pub fn main() !void { pub fn main() void {
seed = @bitCast(std.time.milliTimestamp()); seed = @bitCast(std.time.milliTimestamp());
defer if(global_gpa.deinit() == .leak) { defer if(global_gpa.deinit() == .leak) {
std.log.err("Memory leak", .{}); std.log.err("Memory leak", .{});
@ -666,11 +686,8 @@ pub fn main() !void {
defer sta.deinit(); defer sta.deinit();
stackAllocator = sta.allocator(); stackAllocator = sta.allocator();
// init logging. initLogging();
try std.fs.cwd().makePath("logs"); defer deinitLogging();
logFile = try std.fs.cwd().createFile("logs/latest.log", .{});
defer logFile.close();
supportsANSIColors = std.io.getStdOut().supportsAnsiEscapeCodes();
threadPool = utils.ThreadPool.init(globalAllocator, 1 + ((std.Thread.getCpuCount() catch 4) -| 2)); threadPool = utils.ThreadPool.init(globalAllocator, 1 + ((std.Thread.getCpuCount() catch 4) -| 2));
defer threadPool.deinit(); defer threadPool.deinit();
@ -678,16 +695,16 @@ pub fn main() !void {
settings.init(); settings.init();
defer settings.deinit(); defer settings.deinit();
try Window.init(); Window.init();
defer Window.deinit(); defer Window.deinit();
try graphics.init(); graphics.init();
defer graphics.deinit(); defer graphics.deinit();
audio.init() catch std.log.err("Failed to initialize audio. Continuing the game without sounds.", .{}); audio.init() catch std.log.err("Failed to initialize audio. Continuing the game without sounds.", .{});
defer audio.deinit(); defer audio.deinit();
try gui.init(); gui.init();
defer gui.deinit(); defer gui.deinit();
rotation.init(); rotation.init();
@ -702,7 +719,7 @@ pub fn main() !void {
itemdrop.ItemDropRenderer.init(); itemdrop.ItemDropRenderer.init();
defer itemdrop.ItemDropRenderer.deinit(); defer itemdrop.ItemDropRenderer.deinit();
try assets.init(); assets.init();
defer assets.deinit(); defer assets.deinit();
blocks.meshes.init(); blocks.meshes.init();
@ -711,7 +728,7 @@ pub fn main() !void {
renderer.init(); renderer.init();
defer renderer.deinit(); defer renderer.deinit();
try network.init(); network.init();
entity.ClientEntityManager.init(); entity.ClientEntityManager.init();
defer entity.ClientEntityManager.deinit(); defer entity.ClientEntityManager.deinit();

View File

@ -26,9 +26,12 @@ const Socket = struct {
const os = std.os; const os = std.os;
socketID: os.socket_t, socketID: os.socket_t,
fn startup() !void { fn startup() void {
if(builtin.os.tag == .windows) { if(builtin.os.tag == .windows) {
_ = try os.windows.WSAStartup(2, 2); _ = os.windows.WSAStartup(2, 2) catch |err| {
std.log.err("Could not initialize the Windows Socket API: {s}", .{@errorName(err)});
@panic("Could not init networking.");
};
} }
} }
@ -100,8 +103,8 @@ const Socket = struct {
} }
}; };
pub fn init() !void { pub fn init() void {
try Socket.startup(); Socket.startup();
inline for(@typeInfo(Protocols).Struct.decls) |decl| { inline for(@typeInfo(Protocols).Struct.decls) |decl| {
if(@TypeOf(@field(Protocols, decl.name)) == type) { if(@TypeOf(@field(Protocols, decl.name)) == type) {
const id = @field(Protocols, decl.name).id; const id = @field(Protocols, decl.name).id;
@ -493,7 +496,7 @@ pub const ConnectionManager = struct {
} }
} }
fn onReceive(self: *ConnectionManager, data: []const u8, source: Address) !void { fn onReceive(self: *ConnectionManager, data: []const u8, source: Address) void {
std.debug.assert(self.threadId == std.Thread.getCurrentId()); std.debug.assert(self.threadId == std.Thread.getCurrentId());
self.mutex.lock(); self.mutex.lock();
@ -505,7 +508,7 @@ pub const ConnectionManager = struct {
} }
if(conn.remoteAddress.port == source.port) { if(conn.remoteAddress.port == source.port) {
self.mutex.unlock(); self.mutex.unlock();
try conn.receive(data); conn.receive(data);
return; return;
} }
} }
@ -525,7 +528,7 @@ pub const ConnectionManager = struct {
std.log.debug("Message: {any}", .{data}); std.log.debug("Message: {any}", .{data});
} }
pub fn run(self: *ConnectionManager) !void { pub fn run(self: *ConnectionManager) void {
self.threadId = std.Thread.getCurrentId(); self.threadId = std.Thread.getCurrentId();
var sta = utils.StackAllocator.init(main.globalAllocator, 1 << 23); var sta = utils.StackAllocator.init(main.globalAllocator, 1 << 23);
defer sta.deinit(); defer sta.deinit();
@ -536,12 +539,13 @@ pub const ConnectionManager = struct {
self.waitingToFinishReceive.broadcast(); self.waitingToFinishReceive.broadcast();
var source: Address = undefined; var source: Address = undefined;
if(self.socket.receive(&self.receiveBuffer, 100, &source)) |data| { if(self.socket.receive(&self.receiveBuffer, 100, &source)) |data| {
try self.onReceive(data, source); self.onReceive(data, source);
} else |err| { } else |err| {
if(err == error.Timeout) { if(err == error.Timeout) {
// No message within the last ~100 ms. // No message within the last ~100 ms.
} else { } else {
return err; // TODO: Shutdown the game normally. std.log.err("Got error on receive: {s}", .{@errorName(err)});
@panic("Network failed.");
} }
} }
@ -1635,7 +1639,14 @@ pub const Connection = struct {
} }
} }
pub fn receive(self: *Connection, data: []const u8) !void { pub fn receive(self: *Connection, data: []const u8) void {
self.flawedReceive(data) catch |err| {
std.log.err("Got error while processing received network data: {s}", .{@errorName(err)});
self.disconnect();
};
}
pub fn flawedReceive(self: *Connection, data: []const u8) !void {
std.debug.assert(self.manager.threadId == std.Thread.getCurrentId()); std.debug.assert(self.manager.threadId == std.Thread.getCurrentId());
const protocol = data[0]; const protocol = data[0];
if(self.handShakeState.load(.Monotonic) != Protocols.handShake.stepComplete and protocol != Protocols.handShake.id and protocol != Protocols.keepAlive and protocol != Protocols.important) { if(self.handShakeState.load(.Monotonic) != Protocols.handShake.stepComplete and protocol != Protocols.handShake.id and protocol != Protocols.keepAlive and protocol != Protocols.important) {
@ -1683,7 +1694,7 @@ pub const Connection = struct {
if(Protocols.list[protocol]) |prot| { if(Protocols.list[protocol]) |prot| {
try prot(self, data[1..]); try prot(self, data[1..]);
} else { } else {
std.log.warn("Received unknown protocol width id {}", .{protocol}); std.log.warn("Received unknown protocol with id {}", .{protocol});
} }
} }
} }