diff --git a/src/Inventory.zig b/src/Inventory.zig index 58fa7fde0..3f5b6bbb9 100644 --- a/src/Inventory.zig +++ b/src/Inventory.zig @@ -2132,25 +2132,6 @@ pub fn canHold(self: Inventory, sourceStack: ItemStack) bool { return false; } -// TODO: Remove after #480 -pub fn loadFromZon(self: Inventory, zon: ZonElement) void { - for(self._items, 0..) |*stack, i| { - stack.clear(); - var buf: [1024]u8 = undefined; - const stackZon = zon.getChild(buf[0..std.fmt.printInt(&buf, i, 10, .lower, .{})]); - if(stackZon == .object) { - stack.item = Item.init(stackZon) catch |err| { - const msg = stackZon.toStringEfficient(main.stackAllocator, ""); - defer main.stackAllocator.free(msg); - std.log.err("Couldn't find item {s}: {s}", .{msg, @errorName(err)}); - stack.clear(); - continue; - }; - stack.amount = stackZon.get(u16, "amount", 0); - } - } -} - pub fn toBytes(self: Inventory, writer: *BinaryWriter) void { writer.writeVarInt(u32, @intCast(self._items.len)); for(self._items) |stack| { diff --git a/src/game.zig b/src/game.zig index 71ba4b369..969295de9 100644 --- a/src/game.zig +++ b/src/game.zig @@ -449,7 +449,6 @@ pub const Player = struct { // MARK: Player fn loadFrom(zon: ZonElement) void { super.loadFrom(zon); - inventory.loadFromZon(zon.getChild("inventory")); } pub fn setPosBlocking(newPos: Vec3d) void { diff --git a/src/json.zig b/src/json.zig deleted file mode 100644 index 9be4a0dcd..000000000 --- a/src/json.zig +++ /dev/null @@ -1,773 +0,0 @@ -const std = @import("std"); - -const main = @import("main"); -const NeverFailingAllocator = main.heap.NeverFailingAllocator; -const List = main.List; - -const JsonType = enum(u8) { - JsonInt, - JsonFloat, - JsonString, - JsonStringOwned, - JsonBool, - JsonNull, - JsonArray, - JsonObject, -}; - -pub const JsonElement = union(JsonType) { // MARK: JsonElement - JsonInt: i64, - JsonFloat: f64, - JsonString: []const u8, - JsonStringOwned: []const u8, - JsonBool: bool, - JsonNull: void, - JsonArray: *List(JsonElement), - JsonObject: *std.StringHashMap(JsonElement), - - pub fn initObject(allocator: NeverFailingAllocator) JsonElement { - const map = allocator.create(std.StringHashMap(JsonElement)); - map.* = .init(allocator.allocator); - return JsonElement{.JsonObject = map}; - } - - pub fn initArray(allocator: NeverFailingAllocator) JsonElement { - const list = allocator.create(List(JsonElement)); - list.* = .init(allocator); - return JsonElement{.JsonArray = list}; - } - - pub fn getAtIndex(self: *const JsonElement, comptime _type: type, index: usize, replacement: _type) _type { - if(self.* != .JsonArray) { - return replacement; - } else { - if(index < self.JsonArray.items.len) { - return self.JsonArray.items[index].as(_type, replacement); - } else { - return replacement; - } - } - } - - pub fn getChildAtIndex(self: *const JsonElement, index: usize) JsonElement { - if(self.* != .JsonArray) { - return JsonElement{.JsonNull = {}}; - } else { - if(index < self.JsonArray.items.len) { - return self.JsonArray.items[index]; - } else { - return JsonElement{.JsonNull = {}}; - } - } - } - - pub fn get(self: *const JsonElement, comptime _type: type, key: []const u8, replacement: _type) _type { - if(self.* != .JsonObject) { - return replacement; - } else { - if(self.JsonObject.get(key)) |elem| { - return elem.as(_type, replacement); - } else { - return replacement; - } - } - } - - pub fn getChild(self: *const JsonElement, key: []const u8) JsonElement { - if(self.* != .JsonObject) { - return JsonElement{.JsonNull = {}}; - } else { - if(self.JsonObject.get(key)) |elem| { - return elem; - } else { - return JsonElement{.JsonNull = {}}; - } - } - } - - pub fn as(self: *const JsonElement, comptime T: type, replacement: T) T { - comptime var typeInfo: std.builtin.Type = @typeInfo(T); - comptime var innerType = T; - inline while(typeInfo == .optional) { - innerType = typeInfo.optional.child; - typeInfo = @typeInfo(innerType); - } - switch(typeInfo) { - .int => { - switch(self.*) { - .JsonInt => return std.math.cast(innerType, self.JsonInt) orelse replacement, - .JsonFloat => return std.math.lossyCast(innerType, std.math.round(self.JsonFloat)), - else => return replacement, - } - }, - .float => { - switch(self.*) { - .JsonInt => return @floatFromInt(self.JsonInt), - .JsonFloat => return @floatCast(self.JsonFloat), - else => return replacement, - } - }, - .vector => { - const len = typeInfo.vector.len; - const elems = self.toSlice(); - if(elems.len != len) return replacement; - var result: innerType = undefined; - if(innerType == T) result = replacement; - inline for(0..len) |i| { - if(innerType == T) { - result[i] = elems[i].as(typeInfo.vector.child, result[i]); - } else { - result[i] = elems[i].as(?typeInfo.vector.child, null) orelse return replacement; - } - } - return result; - }, - else => { - switch(innerType) { - []const u8 => { - switch(self.*) { - .JsonString => return self.JsonString, - .JsonStringOwned => return self.JsonStringOwned, - else => return replacement, - } - }, - bool => { - switch(self.*) { - .JsonBool => return self.JsonBool, - else => return replacement, - } - }, - else => { - @compileError("Unsupported type '" ++ @typeName(T) ++ "'."); - }, - } - }, - } - } - - fn createElementFromRandomType(value: anytype, allocator: std.mem.Allocator) JsonElement { - switch(@typeInfo(@TypeOf(value))) { - .void => return JsonElement{.JsonNull = {}}, - .null => return JsonElement{.JsonNull = {}}, - .bool => return JsonElement{.JsonBool = value}, - .int, .comptime_int => return JsonElement{.JsonInt = @intCast(value)}, - .float, .comptime_float => return JsonElement{.JsonFloat = @floatCast(value)}, - .@"union" => { - if(@TypeOf(value) == JsonElement) { - return value; - } else { - @compileError("Unknown value type."); - } - }, - .pointer => |ptr| { - if(ptr.child == u8 and ptr.size == .Slice) { - return JsonElement{.JsonString = value}; - } else { - const childInfo = @typeInfo(ptr.child); - if(ptr.size == .One and childInfo == .array and childInfo.array.child == u8) { - return JsonElement{.JsonString = value}; - } else { - @compileError("Unknown value type."); - } - } - }, - .optional => { - if(value) |val| { - return createElementFromRandomType(val, allocator); - } else { - return JsonElement{.JsonNull = {}}; - } - }, - .vector => { - const len = @typeInfo(@TypeOf(value)).vector.len; - const result = initArray(main.heap.NeverFailingAllocator{.allocator = allocator, .IAssertThatTheProvidedAllocatorCantFail = {}}); - result.JsonArray.ensureCapacity(len); - inline for(0..len) |i| { - result.JsonArray.appendAssumeCapacity(createElementFromRandomType(value[i], allocator)); - } - return result; - }, - else => { - if(@TypeOf(value) == JsonElement) { - return value; - } else { - @compileError("Unknown value type."); - } - }, - } - } - - pub fn put(self: *const JsonElement, key: []const u8, value: anytype) void { - const result = createElementFromRandomType(value, self.JsonObject.allocator); - self.JsonObject.put(self.JsonObject.allocator.dupe(u8, key) catch unreachable, result) catch unreachable; - } - - pub fn putOwnedString(self: *const JsonElement, key: []const u8, value: []const u8) void { - const result = JsonElement{.JsonStringOwned = self.JsonObject.allocator.dupe(u8, value) catch unreachable}; - self.JsonObject.put(self.JsonObject.allocator.dupe(u8, key) catch unreachable, result) catch unreachable; - } - - pub fn toSlice(self: *const JsonElement) []JsonElement { - switch(self.*) { - .JsonArray => |arr| { - return arr.items; - }, - else => return &[0]JsonElement{}, - } - } - - pub fn free(self: *const JsonElement, allocator: NeverFailingAllocator) void { - switch(self.*) { - .JsonInt, .JsonFloat, .JsonBool, .JsonNull, .JsonString => return, - .JsonStringOwned => { - allocator.free(self.JsonStringOwned); - }, - .JsonArray => { - for(self.JsonArray.items) |*elem| { - elem.free(allocator); - } - self.JsonArray.clearAndFree(); - allocator.destroy(self.JsonArray); - }, - .JsonObject => { - var iterator = self.JsonObject.iterator(); - while(true) { - const elem = iterator.next() orelse break; - allocator.free(elem.key_ptr.*); - elem.value_ptr.free(allocator); - } - self.JsonObject.clearAndFree(); - allocator.destroy(self.JsonObject); - }, - } - } - - pub fn isNull(self: *const JsonElement) bool { - return self.* == .JsonNull; - } - - fn escape(list: *List(u8), string: []const u8) void { - for(string) |char| { - switch(char) { - '\\' => list.appendSlice("\\\\"), - '\n' => list.appendSlice("\\n"), - '\"' => list.appendSlice("\\\""), - '\t' => list.appendSlice("\\t"), - else => list.append(char), - } - } - } - fn writeTabs(list: *List(u8), tabs: u32) void { - for(0..tabs) |_| { - list.append('\t'); - } - } - fn recurseToString(json: JsonElement, list: *List(u8), tabs: u32, comptime visualCharacters: bool) void { - switch(json) { - .JsonInt => |value| { - list.writer().print("{d}", .{value}) catch unreachable; - }, - .JsonFloat => |value| { - list.writer().print("{e}", .{value}) catch unreachable; - }, - .JsonBool => |value| { - if(value) { - list.appendSlice("true"); - } else { - list.appendSlice("false"); - } - }, - .JsonNull => { - list.appendSlice("null"); - }, - .JsonString, .JsonStringOwned => |value| { - list.append('\"'); - escape(list, value); - list.append('\"'); - }, - .JsonArray => |array| { - list.append('['); - for(array.items, 0..) |elem, i| { - if(i != 0) { - list.append(','); - } - if(visualCharacters) list.append('\n'); - if(visualCharacters) writeTabs(list, tabs + 1); - recurseToString(elem, list, tabs + 1, visualCharacters); - } - if(visualCharacters) list.append('\n'); - if(visualCharacters) writeTabs(list, tabs); - list.append(']'); - }, - .JsonObject => |obj| { - list.append('{'); - var iterator = obj.iterator(); - var first: bool = true; - while(true) { - const elem = iterator.next() orelse break; - if(!first) { - list.append(','); - } - if(visualCharacters) list.append('\n'); - if(visualCharacters) writeTabs(list, tabs + 1); - list.append('\"'); - list.appendSlice(elem.key_ptr.*); - list.append('\"'); - if(visualCharacters) list.append(' '); - list.append(':'); - if(visualCharacters) list.append(' '); - - recurseToString(elem.value_ptr.*, list, tabs + 1, visualCharacters); - first = false; - } - if(visualCharacters) list.append('\n'); - if(visualCharacters) writeTabs(list, tabs); - list.append('}'); - }, - } - } - pub fn toString(json: JsonElement, allocator: NeverFailingAllocator) []const u8 { - var string = List(u8).init(allocator); - recurseToString(json, &string, 0, true); - return string.toOwnedSlice(); - } - - /// Ignores all the visual characters(spaces, tabs and newlines) and allows adding a custom prefix(which is for example required by networking). - pub fn toStringEfficient(json: JsonElement, allocator: NeverFailingAllocator, prefix: []const u8) []const u8 { - var string = List(u8).init(allocator); - string.appendSlice(prefix); - recurseToString(json, &string, 0, false); - return string.toOwnedSlice(); - } - - pub fn parseFromString(allocator: NeverFailingAllocator, string: []const u8) JsonElement { - var index: u32 = 0; - Parser.skipWhitespaces(string, &index); - return Parser.parseElement(allocator, string, &index); - } -}; - -const Parser = struct { // MARK: Parser - /// All whitespaces from unicode 14. - const whitespaces = [_][]const u8{"\u{0009}", "\u{000A}", "\u{000B}", "\u{000C}", "\u{000D}", "\u{0020}", "\u{0085}", "\u{00A0}", "\u{1680}", "\u{2000}", "\u{2001}", "\u{2002}", "\u{2003}", "\u{2004}", "\u{2005}", "\u{2006}", "\u{2007}", "\u{2008}", "\u{2009}", "\u{200A}", "\u{2028}", "\u{2029}", "\u{202F}", "\u{205F}", "\u{3000}"}; - - fn skipWhitespaces(chars: []const u8, index: *u32) void { - outerLoop: while(index.* < chars.len) { - whitespaceLoop: for(whitespaces) |whitespace| { - for(whitespace, 0..) |char, i| { - if(char != chars[index.* + i]) { - continue :whitespaceLoop; - } - } - index.* += @intCast(whitespace.len); - continue :outerLoop; - } - // Next character is no whitespace. - return; - } - } - - /// Assumes that the region starts with a number character ('+', '-', '.' or a digit). - fn parseNumber(chars: []const u8, index: *u32) JsonElement { - var sign: i2 = 1; - if(chars[index.*] == '-') { - sign = -1; - index.* += 1; - } else if(chars[index.*] == '+') { - index.* += 1; - } - var intPart: i64 = 0; - if(index.* + 1 < chars.len and chars[index.*] == '0' and chars[index.* + 1] == 'x') { - // Parse hex int - index.* += 2; - while(index.* < chars.len) : (index.* += 1) { - switch(chars[index.*]) { - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' => { - intPart = (chars[index.*] - '0') +% intPart*%16; - }, - 'a', 'b', 'c', 'd', 'e', 'f' => { - intPart = (chars[index.*] - 'a' + 10) +% intPart*%16; - }, - 'A', 'B', 'C', 'D', 'E', 'F' => { - intPart = (chars[index.*] - 'A' + 10) +% intPart*%16; - }, - else => { - break; - }, - } - } - return JsonElement{.JsonInt = sign*intPart}; - } - while(index.* < chars.len) : (index.* += 1) { - switch(chars[index.*]) { - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' => { - intPart = (chars[index.*] - '0') +% intPart*%10; - }, - else => { - break; - }, - } - } - if(index.* >= chars.len or (chars[index.*] != '.' and chars[index.*] != 'e' and chars[index.*] != 'E')) { // This is an int - return JsonElement{.JsonInt = sign*intPart}; - } - // So this is a float apparently. - - var floatPart: f64 = 0; - var currentFactor: f64 = 0.1; - if(chars[index.*] == '.') { - index.* += 1; - while(index.* < chars.len) : (index.* += 1) { - switch(chars[index.*]) { - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' => { - floatPart += @as(f64, @floatFromInt(chars[index.*] - '0'))*currentFactor; - currentFactor *= 0.1; - }, - else => { - break; - }, - } - } - } - var exponent: i64 = 0; - var exponentSign: i2 = 1; - if(index.* < chars.len and (chars[index.*] == 'e' or chars[index.*] == 'E')) { - index.* += 1; - if(index.* < chars.len and chars[index.*] == '-') { - exponentSign = -1; - index.* += 1; - } else if(index.* < chars.len and chars[index.*] == '+') { - index.* += 1; - } - while(index.* < chars.len) : (index.* += 1) { - switch(chars[index.*]) { - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' => { - exponent = (chars[index.*] - '0') +% exponent*%10; - }, - else => { - break; - }, - } - } - } - return JsonElement{.JsonFloat = @as(f64, @floatFromInt(sign))*(@as(f64, @floatFromInt(intPart)) + floatPart)*std.math.pow(f64, 10, @as(f64, @floatFromInt(exponentSign*exponent)))}; - } - - fn parseString(allocator: NeverFailingAllocator, chars: []const u8, index: *u32) []const u8 { - var builder = List(u8).init(allocator); - while(index.* < chars.len) : (index.* += 1) { - if(chars[index.*] == '\"') { - index.* += 1; - break; - } else if(chars[index.*] == '\\') { - index.* += 1; - if(index.* >= chars.len) - break; - switch(chars[index.*]) { - 't' => { - builder.append('\t'); - }, - 'n' => { - builder.append('\n'); - }, - 'r' => { - builder.append('\r'); - }, - else => { - builder.append(chars[index.*]); - }, - } - } else { - builder.append(chars[index.*]); - } - } - return builder.toOwnedSlice(); - } - - fn parseArray(allocator: NeverFailingAllocator, chars: []const u8, index: *u32) JsonElement { - const list = allocator.create(List(JsonElement)); - list.* = .init(allocator); - while(index.* < chars.len) { - skipWhitespaces(chars, index); - if(index.* >= chars.len) break; - if(chars[index.*] == ']') { - index.* += 1; - return JsonElement{.JsonArray = list}; - } - list.append(parseElement(allocator, chars, index)); - skipWhitespaces(chars, index); - if(index.* < chars.len and chars[index.*] == ',') { - index.* += 1; - } - } - printError(chars, index.*, "Unexpected end of file in array parsing."); - return JsonElement{.JsonArray = list}; - } - - fn parseObject(allocator: NeverFailingAllocator, chars: []const u8, index: *u32) JsonElement { - const map = allocator.create(std.StringHashMap(JsonElement)); - map.* = .init(allocator.allocator); - while(index.* < chars.len) { - skipWhitespaces(chars, index); - if(index.* >= chars.len) break; - if(chars[index.*] == '}') { - index.* += 1; - return JsonElement{.JsonObject = map}; - } else if(chars[index.*] != '\"') { - printError(chars, index.*, "Unexpected character in object parsing."); - index.* += 1; - continue; - } - index.* += 1; - const key: []const u8 = parseString(allocator, chars, index); - skipWhitespaces(chars, index); - while(index.* < chars.len and chars[index.*] != ':') { - printError(chars, index.*, "Unexpected character in object parsing, expected ':'."); - index.* += 1; - } - index.* += 1; - skipWhitespaces(chars, index); - const value: JsonElement = parseElement(allocator, chars, index); - if(map.fetchPut(key, value) catch unreachable) |old| { - printError(chars, index.*, "Duplicate key."); - allocator.free(old.key); - old.value.free(allocator); - } - skipWhitespaces(chars, index); - if(index.* < chars.len and chars[index.*] == ',') { - index.* += 1; - } - } - printError(chars, index.*, "Unexpected end of file in object parsing."); - return JsonElement{.JsonObject = map}; - } - - fn printError(chars: []const u8, index: u32, msg: []const u8) void { - var lineNumber: u32 = 1; - var lineStart: u32 = 0; - var i: u32 = 0; - while(i < index and i < chars.len) : (i += 1) { - if(chars[i] == '\n') { - lineNumber += 1; - lineStart = i; - } - } - while(i < chars.len) : (i += 1) { - if(chars[i] == '\n') { - break; - } - } - const lineEnd: u32 = i; - std.log.warn("Error in line {}: {s}", .{lineNumber, msg}); - std.log.warn("{s}", .{chars[lineStart..lineEnd]}); - // Mark the position: - var message: [512]u8 = undefined; - i = lineStart; - var outputI: u32 = 0; - while(i < index and i < chars.len) : (i += 1) { - if((chars[i] & 128) != 0 and (chars[i] & 64) == 0) { - // Not the start of a utf8 character - continue; - } - if(chars[i] == '\t') { - message[outputI] = '\t'; - } else { - message[outputI] = ' '; - } - outputI += 1; - if(outputI >= message.len) { - return; // 512 characters is too long for this output to be helpful. - } - } - message[outputI] = '^'; - outputI += 1; - std.log.warn("{s}", .{message[0..outputI]}); - } - - /// Assumes that the region starts with a non-space character. - fn parseElement(allocator: NeverFailingAllocator, chars: []const u8, index: *u32) JsonElement { - if(index.* >= chars.len) { - printError(chars, index.*, "Unexpected end of file."); - return JsonElement{.JsonNull = {}}; - } - switch(chars[index.*]) { - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '-', '.' => { - return parseNumber(chars, index); - }, - 't' => { // Value can only be true. - if(index.* + 3 >= chars.len) { - printError(chars, index.*, "Unexpected end of file."); - } else if(chars[index.* + 1] != 'r' or chars[index.* + 2] != 'u' or chars[index.* + 3] != 'e') { - printError(chars, index.*, "Unknown expression, interpreting as true."); - } - index.* += 4; - return JsonElement{.JsonBool = true}; - }, - 'f' => { // Value can only be false. - if(index.* + 4 >= chars.len) { - printError(chars, index.*, "Unexpected end of file."); - } else if(chars[index.* + 1] != 'a' or chars[index.* + 2] != 'l' or chars[index.* + 3] != 's' or chars[index.* + 4] != 'e') { - printError(chars, index.*, "Unknown expression, interpreting as false."); - } - index.* += 5; - return JsonElement{.JsonBool = false}; - }, - 'n' => { // Value can only be null. - if(index.* + 3 >= chars.len) { - printError(chars, index.*, "Unexpected end of file."); - } else if(chars[index.* + 1] != 'u' or chars[index.* + 2] != 'l' or chars[index.* + 3] != 'l') { - printError(chars, index.*, "Unknown expression, interpreting as null."); - } - index.* += 4; - return JsonElement{.JsonNull = {}}; - }, - '\"' => { - index.* += 1; - return JsonElement{.JsonStringOwned = parseString(allocator, chars, index)}; - }, - '[' => { - index.* += 1; - return parseArray(allocator, chars, index); - }, - '{' => { - index.* += 1; - return parseObject(allocator, chars, index); - }, - else => { - printError(chars, index.*, "Unexpected character."); - return JsonElement{.JsonNull = {}}; - }, - } - } -}; - -// –––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– -// MARK: Testing -// –––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– - -test "skipWhitespaces" { - var index: u32 = 0; - var testString: []const u8 = " fbdn "; - Parser.skipWhitespaces(testString, &index); - try std.testing.expectEqual(index, 2); - testString = "\nĦŊ@Λħŋ"; - index = 0; - Parser.skipWhitespaces(testString, &index); - try std.testing.expectEqual(index, 1); - testString = "\tβρδ→øβν"; - index = 0; - Parser.skipWhitespaces(testString, &index); - try std.testing.expectEqual(index, 1); - testString = "\t \n \t a lot of whitespaces"; - index = 0; - Parser.skipWhitespaces(testString, &index); - try std.testing.expectEqual(index, 8); - testString = " unicode whitespace"; - index = 0; - Parser.skipWhitespaces(testString, &index); - try std.testing.expectEqual(index, 3); - testString = "starting in the middle"; - index = 8; - Parser.skipWhitespaces(testString, &index); - try std.testing.expectEqual(index, 13); -} - -test "number parsing" { - // Integers: - var index: u32 = 0; - try std.testing.expectEqual(Parser.parseNumber("0", &index), JsonElement{.JsonInt = 0}); - index = 0; - try std.testing.expectEqual(Parser.parseNumber("+0", &index), JsonElement{.JsonInt = 0}); - index = 0; - try std.testing.expectEqual(Parser.parseNumber("abcd", &index), JsonElement{.JsonInt = 0}); - index = 0; - try std.testing.expectEqual(Parser.parseNumber("-0+1", &index), JsonElement{.JsonInt = 0}); - index = 5; - try std.testing.expectEqual(Parser.parseNumber(" abcd185473896", &index), JsonElement{.JsonInt = 185473896}); - index = 0; - try std.testing.expectEqual(Parser.parseNumber("0xff34786056.0", &index), JsonElement{.JsonInt = 0xff34786056}); - // Floats: - index = 0; - try std.testing.expectEqual(Parser.parseNumber("0.0", &index), JsonElement{.JsonFloat = 0.0}); - index = 0; - try std.testing.expectEqual(Parser.parseNumber("0e10e10", &index), JsonElement{.JsonFloat = 0.0}); - index = 0; - try std.testing.expectEqual(Parser.parseNumber("-0.0.0", &index), JsonElement{.JsonFloat = 0.0}); - index = 0; - try std.testing.expectEqual(Parser.parseNumber("0xabcd.0e10+-+-", &index), JsonElement{.JsonInt = 0xabcd}); - index = 0; - try std.testing.expectApproxEqAbs(Parser.parseNumber("1.234589e10", &index).JsonFloat, 1.234589e10, 1.0); - index = 5; - try std.testing.expectApproxEqAbs(Parser.parseNumber("_____0.0000000000234589e10abcdfe", &index).JsonFloat, 0.234589, 1e-10); -} - -test "element parsing" { - var wrap = main.heap.ErrorHandlingAllocator.init(std.testing.allocator); - const allocator = wrap.allocator(); - // Integers: - var index: u32 = 0; - try std.testing.expectEqual(Parser.parseElement(allocator, "0", &index), JsonElement{.JsonInt = 0}); - index = 0; - try std.testing.expectEqual(Parser.parseElement(allocator, "0xff34786056.0, true", &index), JsonElement{.JsonInt = 0xff34786056}); - // Floats: - index = 9; - try std.testing.expectEqual(Parser.parseElement(allocator, "{\"abcd\": 0.0,}", &index), JsonElement{.JsonFloat = 0.0}); - index = 0; - try std.testing.expectApproxEqAbs((Parser.parseElement(allocator, "1543.234589e10", &index)).JsonFloat, 1543.234589e10, 1.0); - index = 5; - try std.testing.expectApproxEqAbs((Parser.parseElement(allocator, "_____0.0000000000675849301354e10abcdfe", &index)).JsonFloat, 0.675849301354, 1e-10); - // Null: - index = 0; - try std.testing.expectEqual(Parser.parseElement(allocator, "null", &index), JsonElement{.JsonNull = {}}); - // true: - index = 0; - try std.testing.expectEqual(Parser.parseElement(allocator, "true", &index), JsonElement{.JsonBool = true}); - // false: - index = 0; - try std.testing.expectEqual(Parser.parseElement(allocator, "false", &index), JsonElement{.JsonBool = false}); - - // String: - index = 0; - var result: JsonElement = Parser.parseElement(allocator, "\"abcd\\\"\\\\ħσ→ ↑Φ∫€ ⌬ ε→Π\"", &index); - try std.testing.expectEqualStrings("abcd\"\\ħσ→ ↑Φ∫€ ⌬ ε→Π", result.as([]const u8, "")); - result.free(allocator); - index = 0; - result = Parser.parseElement(allocator, "\"12345", &index); - try std.testing.expectEqualStrings("12345", result.as([]const u8, "")); - result.free(allocator); - - // Object: - index = 0; - result = Parser.parseElement(allocator, "{\"name\": 1}", &index); - try std.testing.expectEqual(JsonType.JsonObject, std.meta.activeTag(result)); - try std.testing.expectEqual(result.JsonObject.get("name"), JsonElement{.JsonInt = 1}); - result.free(allocator); - index = 0; - result = Parser.parseElement(allocator, "{\"object\":{},}", &index); - try std.testing.expectEqual(JsonType.JsonObject, std.meta.activeTag(result)); - try std.testing.expectEqual(JsonType.JsonObject, std.meta.activeTag(result.JsonObject.get("object") orelse JsonType.JsonNull)); - result.free(allocator); - index = 0; - result = Parser.parseElement(allocator, "{ \"object1\" : \"\" \n, \"object2\" :\t{\n},\"object3\" :1.0e4\t,\"\nobject1\":{},\"\tobject1θ\":[],}", &index); - try std.testing.expectEqual(JsonType.JsonObject, std.meta.activeTag(result)); - try std.testing.expectEqual(JsonType.JsonFloat, std.meta.activeTag(result.JsonObject.get("object3") orelse JsonType.JsonNull)); - try std.testing.expectEqual(JsonType.JsonStringOwned, std.meta.activeTag(result.JsonObject.get("object1") orelse JsonType.JsonNull)); - try std.testing.expectEqual(JsonType.JsonObject, std.meta.activeTag(result.JsonObject.get("\nobject1") orelse JsonType.JsonNull)); - try std.testing.expectEqual(JsonType.JsonArray, std.meta.activeTag(result.JsonObject.get("\tobject1θ") orelse JsonType.JsonNull)); - result.free(allocator); - - //Array: - index = 0; - result = Parser.parseElement(allocator, "[\"name\",1]", &index); - try std.testing.expectEqual(JsonType.JsonArray, std.meta.activeTag(result)); - try std.testing.expectEqual(JsonType.JsonStringOwned, std.meta.activeTag(result.JsonArray.items[0])); - try std.testing.expectEqual(JsonElement{.JsonInt = 1}, result.JsonArray.items[1]); - result.free(allocator); - index = 0; - result = Parser.parseElement(allocator, "[ \"name\"\t1\n, 17.1]", &index); - try std.testing.expectEqual(JsonType.JsonArray, std.meta.activeTag(result)); - try std.testing.expectEqual(JsonType.JsonStringOwned, std.meta.activeTag(result.JsonArray.items[0])); - try std.testing.expectEqual(JsonElement{.JsonInt = 1}, result.JsonArray.items[1]); - try std.testing.expectEqual(JsonElement{.JsonFloat = 17.1}, result.JsonArray.items[2]); - result.free(allocator); -} diff --git a/src/main.zig b/src/main.zig index 7aadccef8..7556e6286 100644 --- a/src/main.zig +++ b/src/main.zig @@ -15,7 +15,6 @@ pub const game = @import("game.zig"); pub const graphics = @import("graphics.zig"); pub const itemdrop = @import("itemdrop.zig"); pub const items = @import("items.zig"); -pub const JsonElement = @import("json.zig").JsonElement; pub const migrations = @import("migrations.zig"); pub const models = @import("models.zig"); pub const network = @import("network.zig"); @@ -455,15 +454,6 @@ pub fn exitToMenu(_: usize) void { shouldExitToMenu.store(true, .monotonic); } -fn isValidIdentifierName(str: []const u8) bool { // TODO: Remove after #480 - if(str.len == 0) return false; - if(!std.ascii.isAlphabetic(str[0]) and str[0] != '_') return false; - for(str[1..]) |c| { - if(!std.ascii.isAlphanumeric(c) and c != '_') return false; - } - return true; -} - fn isHiddenOrParentHiddenPosix(path: []const u8) bool { var iter = std.fs.path.componentIterator(path) catch |err| { std.log.err("Cannot iterate on path {s}: {s}!", .{path, @errorName(err)}); @@ -479,67 +469,6 @@ fn isHiddenOrParentHiddenPosix(path: []const u8) bool { } return false; } -pub fn convertJsonToZon(jsonPath: []const u8) void { // TODO: Remove after #480 - if(isHiddenOrParentHiddenPosix(jsonPath)) { - std.log.info("NOT converting {s}.", .{jsonPath}); - return; - } - std.log.info("Converting {s}:", .{jsonPath}); - const jsonString = files.cubyzDir().read(stackAllocator, jsonPath) catch |err| { - std.log.err("Could convert file {s}: {s}", .{jsonPath, @errorName(err)}); - return; - }; - defer stackAllocator.free(jsonString); - var zonString = List(u8).init(stackAllocator); - defer zonString.deinit(); - std.log.debug("{s}", .{jsonString}); - - var i: usize = 0; - while(i < jsonString.len) : (i += 1) { - switch(jsonString[i]) { - '\"' => { - var j = i + 1; - while(j < jsonString.len and jsonString[j] != '"') : (j += 1) {} - const string = jsonString[i + 1 .. j]; - if(isValidIdentifierName(string)) { - zonString.append('.'); - zonString.appendSlice(string); - } else { - zonString.append('"'); - zonString.appendSlice(string); - zonString.append('"'); - } - i = j; - }, - '[', '{' => { - zonString.append('.'); - zonString.append('{'); - }, - ']', '}' => { - zonString.append('}'); - }, - ':' => { - zonString.append('='); - }, - else => |c| { - zonString.append(c); - }, - } - } - const zonPath = std.fmt.allocPrint(stackAllocator.allocator, "{s}.zig.zon", .{jsonPath[0 .. std.mem.lastIndexOfScalar(u8, jsonPath, '.') orelse unreachable]}) catch unreachable; - defer stackAllocator.free(zonPath); - std.log.info("Outputting to {s}:", .{zonPath}); - std.log.debug("{s}", .{zonString.items}); - files.cubyzDir().write(zonPath, zonString.items) catch |err| { - std.log.err("Got error while writing to file: {s}", .{@errorName(err)}); - return; - }; - std.log.info("Deleting file {s}", .{jsonPath}); - files.cubyzDir().deleteFile(jsonPath) catch |err| { - std.log.err("Got error while deleting file: {s}", .{@errorName(err)}); - return; - }; -} pub fn main() void { // MARK: main() defer if(global_gpa.deinit() == .leak) { @@ -552,27 +481,6 @@ pub fn main() void { // MARK: main() initLogging(); defer deinitLogging(); - if(files.cwd().openFile("settings.json")) |file| blk: { // TODO: Remove after #480 - file.close(); - std.log.warn("Detected old game client. Converting all .json files to .zig.zon", .{}); - var dir = files.cwd().openIterableDir(".") catch |err| { - std.log.err("Could not open game directory to convert json files: {s}. Conversion aborted", .{@errorName(err)}); - break :blk; - }; - defer dir.close(); - - var walker = dir.walk(stackAllocator); - defer walker.deinit(); - while(walker.next() catch |err| { - std.log.err("Got error while iterating through json files directory: {s}", .{@errorName(err)}); - break :blk; - }) |entry| { - if(entry.kind == .file and (std.ascii.endsWithIgnoreCase(entry.basename, ".json") or std.mem.eql(u8, entry.basename, "world.dat")) and !std.ascii.startsWithIgnoreCase(entry.path, "compiler") and !std.ascii.startsWithIgnoreCase(entry.path, ".zig-cache") and !std.ascii.startsWithIgnoreCase(entry.path, ".vscode")) { - convertJsonToZon(entry.path); - } - } - } else |_| {} - std.log.info("Starting game client with version {s}", .{settings.version.version}); gui.initWindowList(); @@ -584,17 +492,6 @@ pub fn main() void { // MARK: main() files.init(); defer files.deinit(); - // Background image migration, should be removed after version 0 (#480) - if(files.cwd().hasDir("assets/backgrounds")) moveBlueprints: { - std.fs.rename(std.fs.cwd(), "assets/backgrounds", files.cubyzDir().dir, "backgrounds") catch |err| { - const notification = std.fmt.allocPrint(stackAllocator.allocator, "Encountered error while moving backgrounds: {s}\nYou may have to move your assets/backgrounds manually to {s}/backgrounds", .{@errorName(err), files.cubyzDirStr()}) catch unreachable; - defer stackAllocator.free(notification); - gui.windowlist.notification.raiseNotification(notification); - break :moveBlueprints; - }; - std.log.info("Moved backgrounds to {s}/backgrounds", .{files.cubyzDirStr()}); - } - settings.init(); defer settings.deinit(); @@ -669,28 +566,6 @@ pub fn main() void { // MARK: main() gui.openWindow("main"); } - // Save migration, should be removed after version 0 (#480) - if(files.cwd().hasDir("saves")) moveSaves: { - std.fs.rename(std.fs.cwd(), "saves", files.cubyzDir().dir, "saves") catch |err| { - const notification = std.fmt.allocPrint(stackAllocator.allocator, "Encountered error while moving saves: {s}\nYou may have to move your saves manually to {s}/saves", .{@errorName(err), files.cubyzDirStr()}) catch unreachable; - defer stackAllocator.free(notification); - gui.windowlist.notification.raiseNotification(notification); - break :moveSaves; - }; - const notification = std.fmt.allocPrint(stackAllocator.allocator, "Your saves have been moved from saves to {s}/saves", .{files.cubyzDirStr()}) catch unreachable; - defer stackAllocator.free(notification); - gui.windowlist.notification.raiseNotification(notification); - } - - // Blueprint migration, should be removed after version 0 (#480) - if(files.cwd().hasDir("blueprints")) moveBlueprints: { - std.fs.rename(std.fs.cwd(), "blueprints", files.cubyzDir().dir, "blueprints") catch |err| { - std.log.err("Encountered error while moving blueprints: {s}\nYou may have to move your blueprints manually to {s}/blueprints", .{@errorName(err), files.cubyzDirStr()}); - break :moveBlueprints; - }; - std.log.info("Moved blueprints to {s}/blueprints", .{files.cubyzDirStr()}); - } - server.terrain.globalInit(); defer server.terrain.globalDeinit(); @@ -797,6 +672,5 @@ pub fn refAllDeclsRecursiveExceptCImports(comptime T: type) void { test "abc" { @setEvalBranchQuota(1000000); refAllDeclsRecursiveExceptCImports(@This()); - _ = @import("json.zig"); _ = @import("zon.zig"); } diff --git a/src/network.zig b/src/network.zig index 7bc1c22d8..bb59e7115 100644 --- a/src/network.zig +++ b/src/network.zig @@ -710,7 +710,6 @@ pub const Protocols = struct { }, .assets => { std.log.info("Received assets.", .{}); - main.files.cwd().deleteTree("serverAssets") catch {}; // Delete the assets created before migration main.files.cubyzDir().deleteTree("serverAssets") catch {}; // Delete old assets. var dir = try main.files.cubyzDir().openDir("serverAssets"); defer dir.close(); diff --git a/src/server/world.zig b/src/server/world.zig index bbb8939c8..293d37242 100644 --- a/src/server/world.zig +++ b/src/server/world.zig @@ -459,34 +459,6 @@ pub const ServerWorld = struct { // MARK: ServerWorld }; pub fn init(path: []const u8, nullGeneratorSettings: ?ZonElement) !*ServerWorld { // MARK: init() - covert_old_worlds: { // TODO: Remove after #480 - const worldDatPath = try std.fmt.allocPrint(main.stackAllocator.allocator, "saves/{s}/world.dat", .{path}); - defer main.stackAllocator.free(worldDatPath); - if(main.files.cubyzDir().openFile(worldDatPath)) |file| { - file.close(); - std.log.warn("Detected old world in saves/{s}. Converting all .json files to .zig.zon", .{path}); - const dirPath = try std.fmt.allocPrint(main.stackAllocator.allocator, "saves/{s}", .{path}); - defer main.stackAllocator.free(dirPath); - var dir = main.files.cubyzDir().openIterableDir(dirPath) catch |err| { - std.log.err("Could not open world directory to convert json files: {s}. Conversion aborted", .{@errorName(err)}); - break :covert_old_worlds; - }; - defer dir.close(); - - var walker = dir.walk(main.stackAllocator); - defer walker.deinit(); - while(walker.next() catch |err| { - std.log.err("Got error while iterating through json files directory: {s}", .{@errorName(err)}); - break :covert_old_worlds; - }) |entry| { - if(entry.kind == .file and (std.ascii.endsWithIgnoreCase(entry.basename, ".json") or std.mem.eql(u8, entry.basename, "world.dat"))) { - const fullPath = std.fmt.allocPrint(main.stackAllocator.allocator, "{s}/{s}", .{dirPath, entry.path}) catch unreachable; - defer main.stackAllocator.free(fullPath); - main.convertJsonToZon(fullPath); - } - } - } else |_| {} - } const self = main.globalAllocator.create(ServerWorld); errdefer main.globalAllocator.destroy(self); self.* = ServerWorld{ @@ -537,78 +509,6 @@ pub const ServerWorld = struct { // MARK: ServerWorld try files.cubyzDir().writeZon(try std.fmt.allocPrint(arena.allocator, "saves/{s}/tool_palette.zig.zon", .{path}), self.toolPalette.storeToZon(arena)); try files.cubyzDir().writeZon(try std.fmt.allocPrint(arena.allocator, "saves/{s}/biome_palette.zig.zon", .{path}), self.biomePalette.storeToZon(arena)); - convert_player_data_to_binary: { // TODO: Remove after #480 - std.log.debug("Migrating old player inventory format to binary.", .{}); - - const playerDataPath = std.fmt.allocPrint(main.stackAllocator.allocator, "saves/{s}/players", .{path}) catch unreachable; - defer main.stackAllocator.free(playerDataPath); - - var playerDataDirectory = main.files.cubyzDir().openIterableDir(playerDataPath) catch break :convert_player_data_to_binary; - defer playerDataDirectory.close(); - - { - var walker = playerDataDirectory.walk(main.stackAllocator); - defer walker.deinit(); - - while(walker.next() catch |err| { - std.log.err("Couldn't fetch next directory entry due to an error: {s}", .{@errorName(err)}); - break :convert_player_data_to_binary; - }) |entry| { - if(entry.kind != .file) continue; - if(!std.ascii.endsWithIgnoreCase(entry.basename, ".zon")) continue; - - const absolutePath = std.fmt.allocPrint(main.stackAllocator.allocator, "{s}/{s}", .{playerDataPath, entry.path}) catch unreachable; - defer main.stackAllocator.free(absolutePath); - - const playerData = files.cubyzDir().readToZon(main.stackAllocator, absolutePath) catch |err| { - std.log.err("Could not read player data file '{s}'': {s}.", .{absolutePath, @errorName(err)}); - continue; - }; - defer playerData.deinit(main.stackAllocator); - - const entryKeys: [2][]const u8 = .{ - "playerInventory", - "hand", - }; - for(entryKeys) |key| { - const zon = playerData.getChild(key); - switch(zon) { - .object => { - std.log.debug("Migrating inventory '{s}' '{s}'", .{key, absolutePath}); - - var temp: main.items.Inventory = undefined; - temp._items = main.stackAllocator.alloc(ItemStack, zon.get(u32, "capacity", 0)); - defer main.stackAllocator.free(temp._items); - - for(temp._items) |*stack| stack.* = ItemStack{}; - defer for(temp._items) |*stack| stack.deinit(); - - temp.loadFromZon(zon); - - for(temp._items, 0..) |*stack, i| { - std.log.debug("Item #{}: {} x {s}", .{i, stack.amount, if(stack.item) |item| item.id() else "null"}); - } - - const base64Data = savePlayerInventory(main.stackAllocator, temp); - const old = playerData.object.fetchPut(key, .{.stringOwned = base64Data}) catch unreachable orelse unreachable; - old.value.deinit(main.stackAllocator); - }, - .string, .stringOwned, .null => {}, // Key is skipped - else => |other| { - const representation = zon.toString(main.stackAllocator); - defer main.stackAllocator.free(representation); - std.log.err("Encountered unexpected type ({s}) while migrating '{s}': {s}", .{@tagName(other), absolutePath, representation}); - }, - } - } - files.cubyzDir().writeZon(absolutePath, playerData) catch |err| { - std.log.err("Could not write player data file {s}: {s}.", .{absolutePath, @errorName(err)}); - continue; - }; - } - } - } - var gamerules = files.cubyzDir().readToZon(arena, try std.fmt.allocPrint(arena.allocator, "saves/{s}/gamerules.zig.zon", .{path})) catch ZonElement.initObject(arena); self.defaultGamemode = std.meta.stringToEnum(main.game.Gamemode, gamerules.get([]const u8, "default_gamemode", "creative")) orelse .creative; @@ -704,21 +604,6 @@ pub const ServerWorld = struct { // MARK: ServerWorld }; const ch = ChunkManager.getOrGenerateChunkAndIncreaseRefCount(pos); defer ch.decreaseRefCount(); - if(self.storeMaps and ch.super.pos.voxelSize == 1) { // TODO: Remove after first release - // Store the surrounding map pieces as well: - const mapStartX = ch.super.pos.wx -% main.server.terrain.SurfaceMap.MapFragment.mapSize/2 & ~@as(i32, main.server.terrain.SurfaceMap.MapFragment.mapMask); - const mapStartY = ch.super.pos.wy -% main.server.terrain.SurfaceMap.MapFragment.mapSize/2 & ~@as(i32, main.server.terrain.SurfaceMap.MapFragment.mapMask); - for(0..2) |dx| { - for(0..2) |dy| { - const mapX = mapStartX +% main.server.terrain.SurfaceMap.MapFragment.mapSize*@as(i32, @intCast(dx)); - const mapY = mapStartY +% main.server.terrain.SurfaceMap.MapFragment.mapSize*@as(i32, @intCast(dy)); - const map = main.server.terrain.SurfaceMap.getOrGenerateFragment(mapX, mapY, ch.super.pos.voxelSize); - if(!map.wasStored.swap(true, .monotonic)) { - map.save(null, .{}); - } - } - } - } var nextPos = pos; nextPos.wx &= ~@as(i32, self.pos.voxelSize*chunk.chunkSize); nextPos.wy &= ~@as(i32, self.pos.voxelSize*chunk.chunkSize);