Add the player inventory(no gui yet), fix compile errors from last commit and fix a data race.

This commit is contained in:
IntegratedQuantum 2022-11-22 15:46:04 +01:00
parent 0f0b0b6ac4
commit df36109ee5
10 changed files with 449 additions and 301 deletions

View File

@ -450,7 +450,7 @@ pub const meshing = struct {
pub const ChunkMesh = struct {
pos: ChunkPosition,
size: ChunkCoordinate,
chunk: ?*Chunk,
chunk: std.atomic.Atomic(?*Chunk),
faces: std.ArrayList(u32),
faceData: SSBO,
coreCount: u31 = 0,
@ -465,7 +465,7 @@ pub const meshing = struct {
.pos = pos,
.size = chunkSize*pos.voxelSize,
.faces = std.ArrayList(u32).init(allocator),
.chunk = null,
.chunk = std.atomic.Atomic(?*Chunk).init(null),
.faceData = SSBO.init(),
};
}
@ -473,7 +473,7 @@ pub const meshing = struct {
pub fn deinit(self: *ChunkMesh) void {
self.faceData.deinit();
self.faces.deinit();
if(self.chunk) |ch| {
if(self.chunk.load(.Monotonic)) |ch| {
renderer.RenderStructure.allocator.destroy(ch);
}
}
@ -544,10 +544,10 @@ pub const meshing = struct {
}
}
if(self.chunk) |oldChunk| {
if(self.chunk.load(.Monotonic)) |oldChunk| {
renderer.RenderStructure.allocator.destroy(oldChunk);
}
self.chunk = chunk;
self.chunk.store(chunk, .Monotonic);
self.coreCount = @intCast(u31, self.faces.items.len);
self.neighborStart = [_]u31{self.coreCount} ** 7;
}
@ -625,7 +625,7 @@ pub const meshing = struct {
self.mutex.lock();
defer self.mutex.unlock();
if(!self.generated) return;
const oldBlock = self.chunk.?.blocks[getIndex(x, y, z)];
const oldBlock = self.chunk.load(.Monotonic).?.blocks[getIndex(x, y, z)];
for(Neighbors.iterable) |neighbor| {
var neighborMesh = self;
var nx = x + Neighbors.relX[neighbor];
@ -640,7 +640,7 @@ pub const meshing = struct {
nx &= chunkMask;
ny &= chunkMask;
nz &= chunkMask;
const neighborBlock = neighborMesh.chunk.?.blocks[getIndex(nx, ny, nz)];
const neighborBlock = neighborMesh.chunk.load(.Monotonic).?.blocks[getIndex(nx, ny, nz)];
{
{ // The face of the changed block
const newVisibility = canBeSeenThroughOtherBlock(newBlock, neighborBlock, neighbor);
@ -701,7 +701,7 @@ pub const meshing = struct {
}
if(neighborMesh != self) neighborMesh.uploadData();
}
self.chunk.?.blocks[getIndex(x, y, z)] = newBlock;
self.chunk.load(.Monotonic).?.blocks[getIndex(x, y, z)] = newBlock;
self.uploadData();
}
@ -712,7 +712,7 @@ pub const meshing = struct {
pub fn uploadDataAndFinishNeighbors(self: *ChunkMesh) !void {
std.debug.assert(!self.mutex.tryLock()); // The mutex should be locked when calling this function.
if(self.chunk == null) return; // In the mean-time the mesh was discarded and recreated and all the data was lost.
const chunk = self.chunk.load(.Monotonic) orelse return; // In the mean-time the mesh was discarded and recreated and all the data was lost.
self.faces.shrinkRetainingCapacity(self.coreCount);
for(Neighbors.iterable) |neighbor| {
self.neighborStart[neighbor] = @intCast(u31, self.faces.items.len);
@ -748,8 +748,8 @@ pub const meshing = struct {
var otherX = @intCast(u8, x+%Neighbors.relX[neighbor] & chunkMask);
var otherY = @intCast(u8, y+%Neighbors.relY[neighbor] & chunkMask);
var otherZ = @intCast(u8, z+%Neighbors.relZ[neighbor] & chunkMask);
var block = (&self.chunk.?.blocks)[getIndex(x, y, z)]; // a temporary fix to a compiler performance bug. TODO: check if this was fixed.
var otherBlock = (&neighborMesh.chunk.?.blocks)[getIndex(otherX, otherY, otherZ)]; // a temporary fix to a compiler performance bug. TODO: check if this was fixed.
var block = (&chunk.blocks)[getIndex(x, y, z)]; // a temporary fix to a compiler performance bug. TODO: check if this was fixed.
var otherBlock = (&neighborMesh.chunk.load(.Monotonic).?.blocks)[getIndex(otherX, otherY, otherZ)]; // a temporary fix to a compiler performance bug. TODO: check if this was fixed.
if(canBeSeenThroughOtherBlock(block, otherBlock, neighbor)) {
const normal: u32 = neighbor;
const position: u32 = @as(u32, otherX) | @as(u32, otherY)<<5 | @as(u32, otherZ)<<10 | normal<<24;
@ -810,8 +810,8 @@ pub const meshing = struct {
var otherX = @intCast(u8, (x+%Neighbors.relX[neighbor]+%offsetX >> 1) & chunkMask);
var otherY = @intCast(u8, (y+%Neighbors.relY[neighbor]+%offsetY >> 1) & chunkMask);
var otherZ = @intCast(u8, (z+%Neighbors.relZ[neighbor]+%offsetZ >> 1) & chunkMask);
var block = (&self.chunk.?.blocks)[getIndex(x, y, z)]; // a temporary fix to a compiler performance bug. TODO: check if this was fixed.
var otherBlock = (&neighborMesh.chunk.?.blocks)[getIndex(otherX, otherY, otherZ)]; // a temporary fix to a compiler performance bug. TODO: check if this was fixed.
var block = (&chunk.blocks)[getIndex(x, y, z)]; // a temporary fix to a compiler performance bug. TODO: check if this was fixed.
var otherBlock = (&neighborMesh.chunk.load(.Monotonic).?.blocks)[getIndex(otherX, otherY, otherZ)]; // a temporary fix to a compiler performance bug. TODO: check if this was fixed.
if(canBeSeenThroughOtherBlock(otherBlock, block, neighbor ^ 1)) {
const normal: u32 = neighbor ^ 1;
const position: u32 = @as(u32, x) | @as(u32, y)<<5 | @as(u32, z)<<10 | normal<<24;

View File

@ -2,6 +2,8 @@ const std = @import("std");
const assets = @import("assets.zig");
const chunk = @import("chunk.zig");
const items = @import("items.zig");
const Inventory = items.Inventory;
const json = @import("json.zig");
const JsonElement = json.JsonElement;
const main = @import("main.zig");
@ -46,7 +48,8 @@ pub const Player = struct {
var vel: Vec3d = Vec3d{0, 0, 0};
pub var id: u32 = 0;
pub var isFlying: std.atomic.Atomic(bool) = std.atomic.Atomic(bool).init(true);
var mutex: std.Thread.Mutex = std.Thread.Mutex{};
pub var mutex: std.Thread.Mutex = std.Thread.Mutex{};
pub var inventory__SEND_CHANGES_TO_SERVER: Inventory = undefined;
pub fn setPosBlocking(newPos: Vec3d) void {
mutex.lock();
@ -92,6 +95,7 @@ pub const World = struct {
.name = "client",
.milliTime = std.time.milliTimestamp(),
};
Player.inventory__SEND_CHANGES_TO_SERVER = try Inventory.init(renderer.RenderStructure.allocator, 32);
// TODO:
// super.itemEntityManager = new InterpolatedItemEntityManager(this);
// player = new ClientPlayer(this, 0);
@ -100,6 +104,7 @@ pub const World = struct {
pub fn deinit(self: *World) void {
self.conn.deinit();
Player.inventory__SEND_CHANGES_TO_SERVER.deinit(renderer.RenderStructure.allocator);
}
pub fn finishHandshake(self: *World, jsonObject: JsonElement) !void {

View File

@ -660,6 +660,12 @@ pub const Image = struct {
stb_image.stbi_image_free(data);
return result;
}
pub fn setRGB(self: Image, x: usize, y: usize, rgb: Color) void {
std.debug.assert(x < self.width);
std.debug.assert(y < self.height);
const index = x + y*self.width;
self.imageData[index] = rgb;
}
};
pub const Fog = struct {

View File

@ -5,6 +5,7 @@ const Allocator = std.mem.Allocator;
const blocks = @import("blocks.zig");
const Block = blocks.Block;
const graphics = @import("graphics.zig");
const Color = graphics.Color;
const json = @import("json.zig");
const JsonElement = json.JsonElement;
const main = @import("main.zig");
@ -26,7 +27,7 @@ const Material = struct {
/// How rough the texture should look.
roughness: f32 = undefined,
/// The colors that are used to make tool textures.
colorPalette: []u32 = undefined,
colorPalette: []Color = undefined,
pub fn init(self: *Material, allocator: Allocator, jsonObject: JsonElement) !void {
self.density = jsonObject.get(f32, "density", 1.0);
@ -34,9 +35,15 @@ const Material = struct {
self.power = jsonObject.get(f32, "power", 1.0);
self.roughness = @max(0, jsonObject.get(f32, "roughness", 1.0));
const colors = jsonObject.getChild("colors");
self.colorPalette = try allocator.alloc(u32, colors.JsonArray.items.len);
self.colorPalette = try allocator.alloc(Color, colors.JsonArray.items.len);
for(colors.JsonArray.items) |item, i| {
self.colorPalette[i] = item.as(u32, 0xff000000);
const colorInt = item.as(u32, 0xff000000);
self.colorPalette[i] = Color {
.r = @intCast(u8, colorInt>>16 & 0xff),
.g = @intCast(u8, colorInt>>8 & 0xff),
.b = @intCast(u8, colorInt>>0 & 0xff),
.a = @intCast(u8, colorInt>>24 & 0xff),
};
}
}
@ -157,7 +164,7 @@ const TextureGenerator = struct {
.items = std.ArrayList(*const BaseItem).init(allocator),
};
}
pub fn deinit(self: PixelData) void {
pub fn deinit(self: *PixelData) void {
self.items.clearAndFree();
}
pub fn add(self: *PixelData, item: *const BaseItem, neighbors: u8) !void {
@ -175,180 +182,182 @@ const TextureGenerator = struct {
fn countNeighbors(relativeGrid: *[25]?*const BaseItem) u8 {
var neighbors: u8 = 0;
// direct neighbors count 1.5 times as much.
if (relativeGrid[7]) neighbors += 3;
if (relativeGrid[11]) neighbors += 3;
if (relativeGrid[13]) neighbors += 3;
if (relativeGrid[17]) neighbors += 3;
if(relativeGrid[7] != null) neighbors += 3;
if(relativeGrid[11] != null) neighbors += 3;
if(relativeGrid[13] != null) neighbors += 3;
if(relativeGrid[17] != null) neighbors += 3;
if (relativeGrid[6]) neighbors += 2;
if (relativeGrid[8]) neighbors += 2;
if (relativeGrid[16]) neighbors += 2;
if (relativeGrid[18]) neighbors += 2;
if(relativeGrid[6] != null) neighbors += 2;
if(relativeGrid[8] != null) neighbors += 2;
if(relativeGrid[16] != null) neighbors += 2;
if(relativeGrid[18] != null) neighbors += 2;
return neighbors;
}
/// This part is responsible for associating each pixel with an item.
fn drawRegion(relativeGrid: *[25]?*const BaseItem, relativeNeighborCount: *[25]u8, x: u8, y: u8, pixels: *[16][16]PixelData) void {
fn drawRegion(relativeGrid: *[25]?*const BaseItem, relativeNeighborCount: *[25]u8, x: u8, y: u8, pixels: *[16][16]PixelData) !void {
if(relativeGrid[12]) |item| {
// Count diagonal and straight neighbors:
var diagonalNeighbors: u8 = 0;
var straightNeighbors: u8 = 0;
if (relativeGrid[7]) straightNeighbors += 1;
if (relativeGrid[11]) straightNeighbors += 1;
if (relativeGrid[13]) straightNeighbors += 1;
if (relativeGrid[17]) straightNeighbors += 1;
if(relativeGrid[7] != null) straightNeighbors += 1;
if(relativeGrid[11] != null) straightNeighbors += 1;
if(relativeGrid[13] != null) straightNeighbors += 1;
if(relativeGrid[17] != null) straightNeighbors += 1;
if (relativeGrid[6]) diagonalNeighbors += 1;
if (relativeGrid[8]) diagonalNeighbors += 1;
if (relativeGrid[16]) diagonalNeighbors += 1;
if (relativeGrid[18]) diagonalNeighbors += 1;
if(relativeGrid[6] != null) diagonalNeighbors += 1;
if(relativeGrid[8] != null) diagonalNeighbors += 1;
if(relativeGrid[16] != null) diagonalNeighbors += 1;
if(relativeGrid[18] != null) diagonalNeighbors += 1;
const neighbors = diagonalNeighbors + straightNeighbors;
pixels[x + 1][y + 1].add(item, relativeNeighborCount[12]);
pixels[x + 1][y + 2].add(item, relativeNeighborCount[12]);
pixels[x + 2][y + 1].add(item, relativeNeighborCount[12]);
pixels[x + 2][y + 2].add(item, relativeNeighborCount[12]);
try pixels[x + 1][y + 1].add(item, relativeNeighborCount[12]);
try pixels[x + 1][y + 2].add(item, relativeNeighborCount[12]);
try pixels[x + 2][y + 1].add(item, relativeNeighborCount[12]);
try pixels[x + 2][y + 2].add(item, relativeNeighborCount[12]);
// Checkout straight neighbors:
if(relativeGrid[7]) {
if(relativeGrid[7] != null) {
if(relativeNeighborCount[7] >= relativeNeighborCount[12]) {
pixels[x + 1][y].add(item, relativeNeighborCount[12]);
pixels[x + 2][y].add(item, relativeNeighborCount[12]);
try pixels[x + 1][y].add(item, relativeNeighborCount[12]);
try pixels[x + 2][y].add(item, relativeNeighborCount[12]);
}
if (relativeGrid[1] and !relativeGrid[16] and straightNeighbors <= 1) {
pixels[x + 2][y + 3].add(item, relativeNeighborCount[12]);
if(relativeGrid[1] != null and relativeGrid[16] == null and straightNeighbors <= 1) {
try pixels[x + 2][y + 3].add(item, relativeNeighborCount[12]);
}
if (relativeGrid[3] and !relativeGrid[18] and straightNeighbors <= 1) {
pixels[x + 1][y + 3].add(item, relativeNeighborCount[12]);
if(relativeGrid[3] != null and relativeGrid[18] == null and straightNeighbors <= 1) {
try pixels[x + 1][y + 3].add(item, relativeNeighborCount[12]);
}
}
if (relativeGrid[11]) {
if(relativeGrid[11] != null) {
if(relativeNeighborCount[11] >= relativeNeighborCount[12]) {
pixels[x][y + 1].add(item, relativeNeighborCount[12]);
pixels[x][y + 2].add(item, relativeNeighborCount[12]);
try pixels[x][y + 1].add(item, relativeNeighborCount[12]);
try pixels[x][y + 2].add(item, relativeNeighborCount[12]);
}
if (relativeGrid[5] and !relativeGrid[8] and straightNeighbors <= 1) {
pixels[x + 3][y + 2].add(item, relativeNeighborCount[12]);
if(relativeGrid[5] != null and relativeGrid[8] == null and straightNeighbors <= 1) {
try pixels[x + 3][y + 2].add(item, relativeNeighborCount[12]);
}
if (relativeGrid[15] and !relativeGrid[18] and straightNeighbors <= 1) {
pixels[x + 3][y + 1].add(item, relativeNeighborCount[12]);
if(relativeGrid[15] != null and relativeGrid[18] == null and straightNeighbors <= 1) {
try pixels[x + 3][y + 1].add(item, relativeNeighborCount[12]);
}
}
if (relativeGrid[13]) {
if(relativeGrid[13] != null) {
if(relativeNeighborCount[13] >= relativeNeighborCount[12]) {
pixels[x + 3][y + 1].add(item, relativeNeighborCount[12]);
pixels[x + 3][y + 2].add(item, relativeNeighborCount[12]);
try pixels[x + 3][y + 1].add(item, relativeNeighborCount[12]);
try pixels[x + 3][y + 2].add(item, relativeNeighborCount[12]);
}
if (relativeGrid[9] and !relativeGrid[6] and straightNeighbors <= 1) {
pixels[x][y + 2].add(item, relativeNeighborCount[12]);
if(relativeGrid[9] != null and relativeGrid[6] == null and straightNeighbors <= 1) {
try pixels[x][y + 2].add(item, relativeNeighborCount[12]);
}
if (relativeGrid[19] and !relativeGrid[16] and straightNeighbors <= 1) {
pixels[x][y + 1].add(item, relativeNeighborCount[12]);
if(relativeGrid[19] != null and relativeGrid[16] == null and straightNeighbors <= 1) {
try pixels[x][y + 1].add(item, relativeNeighborCount[12]);
}
}
if (relativeGrid[17]) {
if(relativeGrid[17] != null) {
if(relativeNeighborCount[17] >= relativeNeighborCount[12]) {
pixels[x + 1][y + 3].add(item, relativeNeighborCount[12]);
pixels[x + 2][y + 3].add(item, relativeNeighborCount[12]);
try pixels[x + 1][y + 3].add(item, relativeNeighborCount[12]);
try pixels[x + 2][y + 3].add(item, relativeNeighborCount[12]);
}
if (relativeGrid[21] and !relativeGrid[6] and straightNeighbors <= 1) {
pixels[x + 2][y].add(item, relativeNeighborCount[12]);
if(relativeGrid[21] != null and relativeGrid[6] == null and straightNeighbors <= 1) {
try pixels[x + 2][y].add(item, relativeNeighborCount[12]);
}
if (relativeGrid[23] and !relativeGrid[8] and straightNeighbors <= 1) {
pixels[x + 1][y].add(item, relativeNeighborCount[12]);
if(relativeGrid[23] != null and relativeGrid[8] == null and straightNeighbors <= 1) {
try pixels[x + 1][y].add(item, relativeNeighborCount[12]);
}
}
// Checkout diagonal neighbors:
if (relativeGrid[6]) {
if(relativeGrid[6] != null) {
if(relativeNeighborCount[6] >= relativeNeighborCount[12]) {
pixels[x][y].add(item, relativeNeighborCount[12]);
try pixels[x][y].add(item, relativeNeighborCount[12]);
}
pixels[x + 1][y].add(item, relativeNeighborCount[12]);
pixels[x][y + 1].add(item, relativeNeighborCount[12]);
if (relativeGrid[1] and !relativeGrid[7] and neighbors <= 2) {
pixels[x + 3][y + 2].add(item, relativeNeighborCount[12]);
try pixels[x + 1][y].add(item, relativeNeighborCount[12]);
try pixels[x][y + 1].add(item, relativeNeighborCount[12]);
if(relativeGrid[1] != null and relativeGrid[7] == null and neighbors <= 2) {
try pixels[x + 3][y + 2].add(item, relativeNeighborCount[12]);
}
if (relativeGrid[5] and !relativeGrid[11] and neighbors <= 2) {
pixels[x + 2][y + 3].add(item, relativeNeighborCount[12]);
if(relativeGrid[5] != null and relativeGrid[11] == null and neighbors <= 2) {
try pixels[x + 2][y + 3].add(item, relativeNeighborCount[12]);
}
}
if (relativeGrid[8]) {
if(relativeGrid[8] != null) {
if(relativeNeighborCount[8] >= relativeNeighborCount[12]) {
pixels[x + 3][y].add(item, relativeNeighborCount[12]);
try pixels[x + 3][y].add(item, relativeNeighborCount[12]);
}
pixels[x + 2][y].add(item, relativeNeighborCount[12]);
pixels[x + 3][y + 1].add(item, relativeNeighborCount[12]);
if (relativeGrid[3] and !relativeGrid[7] and neighbors <= 2) {
pixels[x][y + 2].add(item, relativeNeighborCount[12]);
try pixels[x + 2][y].add(item, relativeNeighborCount[12]);
try pixels[x + 3][y + 1].add(item, relativeNeighborCount[12]);
if(relativeGrid[3] != null and relativeGrid[7] == null and neighbors <= 2) {
try pixels[x][y + 2].add(item, relativeNeighborCount[12]);
}
if (relativeGrid[9] and !relativeGrid[13] and neighbors <= 2) {
pixels[x + 1][y + 3].add(item, relativeNeighborCount[12]);
if(relativeGrid[9] != null and relativeGrid[13] == null and neighbors <= 2) {
try pixels[x + 1][y + 3].add(item, relativeNeighborCount[12]);
}
}
if (relativeGrid[16]) {
if(relativeGrid[16] != null) {
if(relativeNeighborCount[16] >= relativeNeighborCount[12]) {
pixels[x][y + 3].add(item, relativeNeighborCount[12]);
try pixels[x][y + 3].add(item, relativeNeighborCount[12]);
}
pixels[x][y + 2].add(item, relativeNeighborCount[12]);
pixels[x + 1][y + 3].add(item, relativeNeighborCount[12]);
if (relativeGrid[21] and !relativeGrid[17] and neighbors <= 2) {
pixels[x + 3][y + 1].add(item, relativeNeighborCount[12]);
try pixels[x][y + 2].add(item, relativeNeighborCount[12]);
try pixels[x + 1][y + 3].add(item, relativeNeighborCount[12]);
if(relativeGrid[21] != null and relativeGrid[17] == null and neighbors <= 2) {
try pixels[x + 3][y + 1].add(item, relativeNeighborCount[12]);
}
if (relativeGrid[15] and !relativeGrid[11] and neighbors <= 2) {
pixels[x + 2][y].add(item, relativeNeighborCount[12]);
if(relativeGrid[15] != null and relativeGrid[11] == null and neighbors <= 2) {
try pixels[x + 2][y].add(item, relativeNeighborCount[12]);
}
}
if (relativeGrid[18]) {
if(relativeGrid[18] != null) {
if(relativeNeighborCount[18] >= relativeNeighborCount[12]) {
pixels[x + 3][y + 3].add(item, relativeNeighborCount[12]);
try pixels[x + 3][y + 3].add(item, relativeNeighborCount[12]);
}
pixels[x + 2][y + 3].add(item, relativeNeighborCount[12]);
pixels[x + 3][y + 2].add(item, relativeNeighborCount[12]);
if (relativeGrid[23] and !relativeGrid[17] and neighbors <= 2) {
pixels[x][y + 1].add(item, relativeNeighborCount[12]);
try pixels[x + 2][y + 3].add(item, relativeNeighborCount[12]);
try pixels[x + 3][y + 2].add(item, relativeNeighborCount[12]);
if(relativeGrid[23] != null and relativeGrid[17] == null and neighbors <= 2) {
try pixels[x][y + 1].add(item, relativeNeighborCount[12]);
}
if (relativeGrid[19] and !relativeGrid[13] and neighbors <= 2) {
pixels[x + 1][y].add(item, relativeNeighborCount[12]);
if(relativeGrid[19] != null and relativeGrid[13] == null and neighbors <= 2) {
try pixels[x + 1][y].add(item, relativeNeighborCount[12]);
}
}
// Make stuff more round when there is many incoming connections:
if(diagonalNeighbors >= 3 or straightNeighbors == 4) {
pixels[x + 0][y + 1].add(item, relativeNeighborCount[12]);
pixels[x + 0][y + 2].add(item, relativeNeighborCount[12]);
pixels[x + 3][y + 1].add(item, relativeNeighborCount[12]);
pixels[x + 3][y + 2].add(item, relativeNeighborCount[12]);
pixels[x + 1][y + 0].add(item, relativeNeighborCount[12]);
pixels[x + 1][y + 3].add(item, relativeNeighborCount[12]);
pixels[x + 2][y + 0].add(item, relativeNeighborCount[12]);
pixels[x + 2][y + 3].add(item, relativeNeighborCount[12]);
try pixels[x + 0][y + 1].add(item, relativeNeighborCount[12]);
try pixels[x + 0][y + 2].add(item, relativeNeighborCount[12]);
try pixels[x + 3][y + 1].add(item, relativeNeighborCount[12]);
try pixels[x + 3][y + 2].add(item, relativeNeighborCount[12]);
try pixels[x + 1][y + 0].add(item, relativeNeighborCount[12]);
try pixels[x + 1][y + 3].add(item, relativeNeighborCount[12]);
try pixels[x + 2][y + 0].add(item, relativeNeighborCount[12]);
try pixels[x + 2][y + 3].add(item, relativeNeighborCount[12]);
// Check which of the neighbors was empty:
if(relativeGrid[6] == null) {
pixels[x + 0][y + 0].add(item, relativeNeighborCount[12]);
pixels[x + 2][y - 1].add(item, relativeNeighborCount[12]);
pixels[x - 1][y + 2].add(item, relativeNeighborCount[12]);
try pixels[x + 0][y + 0].add(item, relativeNeighborCount[12]);
try pixels[x + 2][y - 1].add(item, relativeNeighborCount[12]);
try pixels[x - 1][y + 2].add(item, relativeNeighborCount[12]);
}
if(relativeGrid[8] == null) {
pixels[x + 3][y + 0].add(item, relativeNeighborCount[12]);
pixels[x + 1][y - 1].add(item, relativeNeighborCount[12]);
pixels[x + 4][y + 2].add(item, relativeNeighborCount[12]);
try pixels[x + 3][y + 0].add(item, relativeNeighborCount[12]);
try pixels[x + 1][y - 1].add(item, relativeNeighborCount[12]);
try pixels[x + 4][y + 2].add(item, relativeNeighborCount[12]);
}
if(relativeGrid[16] == null) {
pixels[x + 0][y + 3].add(item, relativeNeighborCount[12]);
pixels[x + 2][y + 4].add(item, relativeNeighborCount[12]);
pixels[x - 1][y + 1].add(item, relativeNeighborCount[12]);
try pixels[x + 0][y + 3].add(item, relativeNeighborCount[12]);
try pixels[x + 2][y + 4].add(item, relativeNeighborCount[12]);
try pixels[x - 1][y + 1].add(item, relativeNeighborCount[12]);
}
if(relativeGrid[18] == null) {
pixels[x + 3][y + 3].add(item, relativeNeighborCount[12]);
pixels[x + 1][y + 4].add(item, relativeNeighborCount[12]);
pixels[x + 4][y + 1].add(item, relativeNeighborCount[12]);
try pixels[x + 3][y + 3].add(item, relativeNeighborCount[12]);
try pixels[x + 1][y + 4].add(item, relativeNeighborCount[12]);
try pixels[x + 4][y + 1].add(item, relativeNeighborCount[12]);
}
}
}
}
fn generateHeightMap(itemGrid: *[16][16]?*BaseItem, seed: *u64) [17][17]f32 {
fn generateHeightMap(itemGrid: *[16][16]?*const BaseItem, seed: *u64) [17][17]f32 {
var heightMap: [17][17]f32 = undefined;
var x: u8 = 0;
while(x < 17) : (x += 1) {
@ -366,7 +375,7 @@ const TextureGenerator = struct {
while(dy <= 0) : (dy += 1) {
if(y + dy < 0 or y + dy >= 16) continue;
const otherItem = itemGrid[@intCast(usize, x + dx)][@intCast(usize, y + dy)];
heightMap[x][y] = if(otherItem) |item| 1 + (4*random.nextFloat(seed) - 2)*item.material.roughness else 0;
heightMap[x][y] = if(otherItem) |item| (if(item.material) |material| 1 + (4*random.nextFloat(seed) - 2)*material.roughness else 0) else 0;
if(otherItem != oneItem) {
hasDifferentItems = true;
}
@ -387,7 +396,7 @@ const TextureGenerator = struct {
if(y + dy < 0 or y + dy >= 16) continue;
const otherItem = itemGrid[@intCast(usize, x + dx)][@intCast(usize, y + dy)];
const dVec = Vec2f{@intToFloat(f32, dx) + 0.5, @intToFloat(f32, dy) + 0.5};
heightMap[x][y] += if(otherItem) 1.0/vec.dot(dVec, dVec) else 0;
heightMap[x][y] += if(otherItem != null) 1.0/vec.dot(dVec, dVec) else 0;
}
}
}
@ -395,7 +404,7 @@ const TextureGenerator = struct {
return heightMap;
}
pub fn generate(tool: *Tool) void {
pub fn generate(tool: *Tool) !void {
const img = tool.texture;
var pixelMaterials: [16][16]PixelData = undefined;
var x: u8 = 0;
@ -462,7 +471,7 @@ const TextureGenerator = struct {
}
}
const index = x + 5*y;
drawRegion(&offsetGrid, &offsetNeighborCount, GRID_CENTERS_X[index] - 2, GRID_CENTERS_Y[index] - 2, pixelMaterials);
try drawRegion(&offsetGrid, &offsetNeighborCount, GRID_CENTERS_X[index] - 2, GRID_CENTERS_Y[index] - 2, &pixelMaterials);
}
}
@ -473,7 +482,9 @@ const TextureGenerator = struct {
while(y < 16) : (y += 1) {
if(pixelMaterials[x][y].items.items.len != 0) {
// Choose a random material at conflict zones:
itemGrid[x][y] = pixelMaterials[x][y].items.items[random.nextIntBounded(u8, &seed, pixelMaterials[x][y].items.items.len)];
itemGrid[x][y] = pixelMaterials[x][y].items.items[random.nextIntBounded(u8, &seed, @intCast(u8, pixelMaterials[x][y].items.items.len))];
} else {
itemGrid[x][y] = null;
}
}
}
@ -489,7 +500,7 @@ const TextureGenerator = struct {
// Calculate the lighting based on the nearest free space:
const lightTL = heightMap[x][y] - heightMap[x + 1][y + 1];
const lightTR = heightMap[x + 1][y] - heightMap[x][y + 1];
const light = 2 - @floatToInt(u32, @round((lightTL * 2 + lightTR) / 6));
var light = 2 - @floatToInt(u32, @round((lightTL * 2 + lightTR) / 6));
light = @max(@min(light, 4), 0);
img.setRGB(x, y, material.colorPalette[light]);
}
@ -513,7 +524,7 @@ const ToolPhysics = struct {
while(y > 0) : (y -= 5) {
var x: u32 = 0;
while(x < 5) : (x += 5) {
if(tool.craftingGrid[y + x]) {
if(tool.craftingGrid[y + x] != null) {
break :outer;
}
}
@ -523,21 +534,21 @@ const ToolPhysics = struct {
// TODO: Add left-hander setting that mirrors the x axis of the tools and the crafting grid
var x: u32 = 4;
while(true) {
if(tool.craftingGrid[y + x]) {
tool.handlePosition.x = TextureGenerator.GRID_CENTERS_X[x + y] - 0.5;
tool.handlePosition.y = TextureGenerator.GRID_CENTERS_Y[x + y] - 0.5;
if(tool.craftingGrid[y + x] != null) {
tool.handlePosition[0] = @intToFloat(f32, TextureGenerator.GRID_CENTERS_X[x + y]) - 0.5;
tool.handlePosition[1] = @intToFloat(f32, TextureGenerator.GRID_CENTERS_Y[x + y]) - 0.5;
// Count the neighbors to determine whether it's a good handle:
var neighbors: u32 = 0;
if(x != 0 and tool.craftingGrid[y + x - 1])
if(x != 0 and tool.craftingGrid[y + x - 1] != null)
neighbors += 1;
if(x != 4 and tool.craftingGrid[y + x + 1])
if(x != 4 and tool.craftingGrid[y + x + 1] != null)
neighbors += 1;
if(y != 0) {
if(tool.craftingGrid[y - 5 + x])
if(tool.craftingGrid[y - 5 + x] != null)
neighbors += 1;
if(x != 0 and tool.craftingGrid[y - 5 + x - 1])
if(x != 0 and tool.craftingGrid[y - 5 + x - 1] != null)
neighbors += 1;
if(x != 4 and tool.craftingGrid[y - 5 + x + 1])
if(x != 4 and tool.craftingGrid[y - 5 + x + 1] != null)
neighbors += 1;
}
if(neighbors <= 1) {
@ -563,8 +574,8 @@ const ToolPhysics = struct {
if(tool.materialGrid[x][y]) |item| {
if(item.material) |material| {
const localMass = material.density;
centerOfMass.x += localMass*(@intToFloat(f32, x) + 0.5);
centerOfMass.y += localMass*(@intToFloat(f32, y) + 0.5);
centerOfMass[0] += localMass*(@intToFloat(f32, x) + 0.5);
centerOfMass[1] += localMass*(@intToFloat(f32, y) + 0.5);
mass += localMass;
}
}
@ -582,8 +593,8 @@ const ToolPhysics = struct {
if(tool.materialGrid[x][y]) |item| {
if(item.material) |material| {
const localMass = material.density;
const dx = @intToFloat(f32, x) + 0.5 - tool.centerOfMass.x;
const dy = @intToFloat(f32, y) + 0.5 - tool.centerOfMass.y;
const dx = @intToFloat(f32, x) + 0.5 - tool.centerOfMass[0];
const dy = @intToFloat(f32, y) + 0.5 - tool.centerOfMass[1];
inertia += localMass*(dx*dx + dy*dy);
}
}
@ -591,21 +602,21 @@ const ToolPhysics = struct {
}
tool.inertiaCenterOfMass = inertia;
// Using the parallel axis theorem the inertia relative to the handle can be derived:
tool.inertiaHandle = inertia + mass * tool.centerOfMass.distance(tool.handlePosition);
tool.inertiaHandle = inertia + mass*vec.length(tool.centerOfMass - tool.handlePosition);
}
/// Determines the sharpness of a point on the tool.
fn determineSharpness(tool: *Tool, point: *Vec3i, initialAngle: f32) void {
const center: Vec2f = tool.handlePosition - vec.normalize(tool.centerOfMass - tool.handlePosition)*16; // Going 16 pixels away from the handle to simulate arm length.
const center: Vec2f = tool.handlePosition - vec.normalize(tool.centerOfMass - tool.handlePosition)*@splat(2, @as(f32, 16)); // Going 16 pixels away from the handle to simulate arm length.
// A region is smooth if there is a lot of pixel within similar angle/distance:
const originalAngle = std.math.atan2(f32, @intToFloat(f32, point.y) + 0.5 - center.y, @intToFloat(f32, point.x) + 0.5 - center.x) - initialAngle;
const originalDistance = @cos(originalAngle)*vec.length(center - Vec2f{@intToFloat(f32, point.x) + 0.5, @intToFloat(f32, point.y) + 0.5});
var numOfSmoothPixels: u32 = 0;
const originalAngle = std.math.atan2(f32, @intToFloat(f32, point.*[1]) + 0.5 - center[1], @intToFloat(f32, point.*[0]) + 0.5 - center[0]) - initialAngle;
const originalDistance = @cos(originalAngle)*vec.length(center - Vec2f{@intToFloat(f32, point.*[0]) + 0.5, @intToFloat(f32, point.*[1]) + 0.5});
var numOfSmoothPixels: u31 = 0;
var x: f32 = 0;
while(x < 16) : (x += 1) {
var y: f32 = 0;
while(y < 16) : (y += 1) {
const angle = std.math.atan2(f32, y + 0.5 - center.y, x + 0.5 - center.x) - initialAngle;
const angle = std.math.atan2(f32, y + 0.5 - center[1], x + 0.5 - center[0]) - initialAngle;
const distance = @cos(angle)*vec.length(center - Vec2f{x + 0.5, y + 0.5});
const deltaAngle = @fabs(angle - originalAngle);
const deltaDist = @fabs(distance - originalDistance);
@ -614,7 +625,7 @@ const ToolPhysics = struct {
}
}
}
point.z = numOfSmoothPixels;
point.*[2] = numOfSmoothPixels;
}
/// Determines where the tool would collide with the terrain.
@ -622,32 +633,32 @@ const ToolPhysics = struct {
fn determineCollisionPoints(tool: *Tool, leftCollisionPoint: *Vec3i, rightCollisionPoint: *Vec3i, frontCollisionPoint: *Vec3i, factor: f32) void {
// For finding that point the center of rotation is assumed to be 1 arm(16 pixel) begind the handle.
// Additionally the handle is assumed to go towards the center of mass.
const center: Vec2f = tool.handlePosition - vec.normalize(tool.centerOfMass - tool.handlePosition)*factor; // Going some distance away from the handle to simulate arm length.
const center: Vec2f = tool.handlePosition - vec.normalize(tool.centerOfMass - tool.handlePosition)*@splat(2, factor); // Going some distance away from the handle to simulate arm length.
// Angle of the handle.
const initialAngle = std.math.atan2(f32, tool.handlePosition.y - center.y, tool.handlePosition.x - center.x);
const initialAngle = std.math.atan2(f32, tool.handlePosition[1] - center[1], tool.handlePosition[0] - center[0]);
var leftCollisionAngle: f32 = 0;
var rightCollisionAngle: f32 = 0;
var frontCollisionDistance: f32 = 0;
var x: i32 = 0;
var x: u8 = 0;
while(x < 16) : (x += 1) {
var y: i32 = 0;
var y: u8 = 0;
while(y < 16) : (y += 1) {
if(!tool.materialGrid[x][y]) continue;
if(tool.materialGrid[x][y] == null) continue;
const x_float = @intToFloat(f32, x);
const y_float = @intToFloat(f32, y);
const angle = std.math.atan2(f32, y_float + 0.5 - center.y, x_float + 0.5 - center.x) - initialAngle;
const angle = std.math.atan2(f32, y_float + 0.5 - center[1], x_float + 0.5 - center[0]) - initialAngle;
const distance = @cos(angle)*vec.length(center - Vec2f{x_float + 0.5, y_float + 0.5});
if(angle < leftCollisionAngle) {
leftCollisionAngle = angle;
leftCollisionPoint = Vec3i{x, y, 0};
leftCollisionPoint.* = Vec3i{x, y, 0};
}
if(angle > rightCollisionAngle) {
rightCollisionAngle = angle;
rightCollisionPoint = Vec3i{x, y, 0};
rightCollisionPoint.* = Vec3i{x, y, 0};
}
if(distance > frontCollisionDistance) {
frontCollisionDistance = distance;
frontCollisionPoint = Vec3i{x, y, 0};
frontCollisionPoint.* = Vec3i{x, y, 0};
}
}
}
@ -673,7 +684,7 @@ const ToolPhysics = struct {
}
}
// Smaller tools are faster to swing. To balance that smaller tools get a lower durability.
tool.maxDurability = @max(1, std.math.pow(f32, durability/4, 1.5));
tool.maxDurability = @floatToInt(u32, @max(1, std.math.pow(f32, durability/4, 1.5)));
tool.durability = tool.maxDurability;
}
@ -685,7 +696,7 @@ const ToolPhysics = struct {
// But when the pickaxe does get heavier 2 things happen:
// 1. The player needs to lift a bigger weight, so the tool speed gets reduced(calculated elsewhere).
// 2. When travelling down the tool also gets additional energy from gravity, so the force is increased by m·g.
impactEnergy *= tool.materialGrid[collisionPoint.x][collisionPoint.y].?.material.?.power + tool.mass/256;
impactEnergy *= tool.materialGrid[@intCast(usize, collisionPoint[0])][@intCast(usize, collisionPoint[1])].?.material.?.power + tool.mass/256;
return impactEnergy; // TODO: Balancing
}
@ -701,27 +712,26 @@ const ToolPhysics = struct {
while(x < 2) : (x += 1) {
var y: i32 = -1;
while(y <= 2) : (y += 1) {
if(x + collisionPointLower.x >= 0 and x + collisionPointLower.x < 16) {
if(y + collisionPointLower.y >= 0 and y + collisionPointLower.y < 16) {
if(tool.materialGrid[x + collisionPointLower.x][y + collisionPointLower.y])
if(x + collisionPointLower[0] >= 0 and x + collisionPointLower[0] < 16) {
if(y + collisionPointLower[1] >= 0 and y + collisionPointLower[1] < 16) {
if(tool.materialGrid[@intCast(usize, x + collisionPointLower[0])][@intCast(usize, y + collisionPointLower[1])] != null)
neighborsLower += 1;
}
}
}
}
var neighborsUpper: u32 = 0;
var dirUpper: Vec2i = Vec2i{0};
// Vector2i dirUpper = new Vector2i();
var dirUpper: Vec2i = Vec2i{0, 0};
x = -1;
while(x < 2) : (x += 1) {
var y: i32 = -1;
while(y <= 2) : (y += 1) {
if(x + collisionPointUpper.x >= 0 and x + collisionPointUpper.x < 16) {
if(y + collisionPointUpper.y >= 0 and y + collisionPointUpper.y < 16) {
if(tool.materialGrid[x + collisionPointUpper.x][y + collisionPointUpper.y]) {
if(x + collisionPointUpper[0] >= 0 and x + collisionPointUpper[0] < 16) {
if(y + collisionPointUpper[1] >= 0 and y + collisionPointUpper[1] < 16) {
if(tool.materialGrid[@intCast(usize, x + collisionPointUpper[0])][@intCast(usize, y + collisionPointUpper[1])] != null) {
neighborsUpper += 1;
dirUpper.x += x;
dirUpper.y += y;
dirUpper[0] += x;
dirUpper[1] += y;
}
}
}
@ -730,7 +740,7 @@ const ToolPhysics = struct {
if(neighborsLower > 3 and neighborsUpper > 3) return 0;
// A pickaxe never points upwards:
if (neighborsUpper == 3 and dirUpper.y == 2) {
if(neighborsUpper == 3 and dirUpper[1] == 2) {
return 0;
}
@ -740,8 +750,8 @@ const ToolPhysics = struct {
/// Determines how good an axe this side of the tool would make.
fn evaluateAxePower(tool: *Tool, collisionPointLower: Vec3i, collisionPointUpper: Vec3i) f32 {
// Axes are used for breaking up wood. This requires a larger area (= smooth tip) rather than a sharp tip.
const collisionPointLowerFloat = Vec2f{@intToFloat(f32, collisionPointLower.x), @intToFloat(f32, collisionPointLower.y)};
const collisionPointUpperFloat = Vec2f{@intToFloat(f32, collisionPointUpper.x), @intToFloat(f32, collisionPointUpper.y)};
const collisionPointLowerFloat = Vec2f{@intToFloat(f32, collisionPointLower[0]), @intToFloat(f32, collisionPointLower[1])};
const collisionPointUpperFloat = Vec2f{@intToFloat(f32, collisionPointUpper[0]), @intToFloat(f32, collisionPointUpper[1])};
const areaFactor = 0.25 + vec.length(collisionPointLowerFloat - collisionPointUpperFloat)/4;
return areaFactor*calculateImpactEnergy(tool, collisionPointLower)/8;
@ -764,7 +774,7 @@ const ToolPhysics = struct {
var y: u8 = 0;
while(y < 16) : (y += 1) {
sandPiles[x][y] = std.math.maxInt(u8);
if (!tool.materialGrid[x][y]) {
if(tool.materialGrid[x][y] == null) {
sandPiles[x][y] = 0;
try stack.append(Entry{.x=x, .y=y});
} else if(x == 0 or x == 15 or y == 0 or y == 15) {
@ -776,28 +786,28 @@ const ToolPhysics = struct {
while(stack.popOrNull()) |entry| {
x = entry.x;
const y = entry.y;
if(x != 0 and y != 0 and tool.materialGrid[x - 1][y - 1]) {
if(x != 0 and y != 0 and tool.materialGrid[x - 1][y - 1] != null) {
if(sandPiles[x - 1][y - 1] > sandPiles[x][y] + 1) {
sandPiles[x - 1][y - 1] = sandPiles[x][y] + 1;
stack.append(Entry{.x=x-1, .y=y-1});
try stack.append(Entry{.x=x-1, .y=y-1});
}
}
if(x != 0 and y != 15 and tool.materialGrid[x - 1][y + 1]) {
if(x != 0 and y != 15 and tool.materialGrid[x - 1][y + 1] != null) {
if(sandPiles[x - 1][y + 1] > sandPiles[x][y] + 1) {
sandPiles[x - 1][y + 1] = sandPiles[x][y] + 1;
stack.append(Entry{.x=x-1, .y=y+1});
try stack.append(Entry{.x=x-1, .y=y+1});
}
}
if(x != 15 and y != 0 and tool.materialGrid[x + 1][y - 1]) {
if(x != 15 and y != 0 and tool.materialGrid[x + 1][y - 1] != null) {
if(sandPiles[x + 1][y - 1] > sandPiles[x][y] + 1) {
sandPiles[x + 1][y - 1] = sandPiles[x][y] + 1;
stack.append(Entry{.x=x+1, .y=y-1});
try stack.append(Entry{.x=x+1, .y=y-1});
}
}
if(x != 15 and y != 15 and tool.materialGrid[x + 1][y + 1]) {
if(x != 15 and y != 15 and tool.materialGrid[x + 1][y + 1] != null) {
if(sandPiles[x + 1][y + 1] > sandPiles[x][y] + 1) {
sandPiles[x + 1][y + 1] = sandPiles[x][y] + 1;
stack.append(Entry{.x=x+1, .y=y+1});
try stack.append(Entry{.x=x+1, .y=y+1});
}
}
}
@ -807,7 +817,7 @@ const ToolPhysics = struct {
while(x < 16) : (x += 1) {
var y: u8 = 0;
while(y < 16) : (y += 1) {
volume += sandPiles[x][y];
volume += @intToFloat(f32, sandPiles[x][y]);
}
}
volume /= 256; // TODO: Balancing
@ -816,28 +826,28 @@ const ToolPhysics = struct {
/// Determines all the basic properties of the tool.
pub fn evaluateTool(tool: *Tool) void {
pub fn evaluateTool(tool: *Tool) !void {
const hasGoodHandle = findHandle(tool);
calculateDurability(tool);
determineInertia(tool);
var leftCollisionPointLower = Vec3i{};
var rightCollisionPointLower = Vec3i{};
var frontCollisionPointLower = Vec3i{};
var leftCollisionPointUpper = Vec3i{};
var rightCollisionPointUpper = Vec3i{};
var frontCollisionPointUpper = Vec3i{};
var leftCollisionPointLower = Vec3i{0, 0, 0};
var rightCollisionPointLower = Vec3i{0, 0, 0};
var frontCollisionPointLower = Vec3i{0, 0, 0};
var leftCollisionPointUpper = Vec3i{0, 0, 0};
var rightCollisionPointUpper = Vec3i{0, 0, 0};
var frontCollisionPointUpper = Vec3i{0, 0, 0};
determineCollisionPoints(tool, &leftCollisionPointLower, &rightCollisionPointLower, &frontCollisionPointLower, 16);
determineCollisionPoints(tool, &rightCollisionPointUpper, &leftCollisionPointUpper, &frontCollisionPointUpper, -20);
const leftPP = evaluatePickaxePower(tool, &leftCollisionPointLower, &leftCollisionPointUpper);
const rightPP = evaluatePickaxePower(tool, &rightCollisionPointLower, &rightCollisionPointUpper);
const leftPP = evaluatePickaxePower(tool, leftCollisionPointLower, leftCollisionPointUpper);
const rightPP = evaluatePickaxePower(tool, rightCollisionPointLower, rightCollisionPointUpper);
tool.pickaxePower = @max(leftPP, rightPP); // TODO: Adjust the swing direction.
const leftAP = evaluateAxePower(tool, &leftCollisionPointLower, &leftCollisionPointUpper);
const rightAP = evaluateAxePower(tool, &rightCollisionPointLower, &rightCollisionPointUpper);
const leftAP = evaluateAxePower(tool, leftCollisionPointLower, leftCollisionPointUpper);
const rightAP = evaluateAxePower(tool, rightCollisionPointLower, rightCollisionPointUpper);
tool.axePower = @max(leftAP, rightAP); // TODO: Adjust the swing direction.
tool.shovelPower = evaluateShovelPower(tool, &frontCollisionPointLower);
tool.shovelPower = try evaluateShovelPower(tool, frontCollisionPointLower);
// It takes longer to swing a heavy tool.
tool.swingTime = (tool.mass + tool.inertiaHandle/8)/256; // TODO: Balancing
@ -884,31 +894,31 @@ const Tool = struct {
/// Moment of inertia relative to the center of mass.
inertiaCenterOfMass: f32,
pub fn init(allocator: Allocator) !*Tool {
var self = try allocator.create(Tool);
self.texture = try graphics.Image.init(allocator, 16, 16);
pub fn init() !*Tool {
var self = try main.globalAllocator.create(Tool);
self.texture = try graphics.Image.init(main.globalAllocator, 16, 16);
return self;
}
pub fn deinit(self: *Tool, allocator: Allocator) void {
allocator.destroy(self);
self.texture.deinit(allocator);
pub fn deinit(self: *const Tool) void {
main.globalAllocator.destroy(self);
self.texture.deinit(main.globalAllocator);
}
pub fn initFromCraftingGrid(allocator: Allocator, craftingGrid: [25]?*const BaseItem, seed: u32) !*Tool {
var self = try init(allocator);
pub fn initFromCraftingGrid(craftingGrid: [25]?*const BaseItem, seed: u32) !*Tool {
var self = try init();
self.seed = seed;
self.craftingGrid = craftingGrid;
// Produce the tool and its textures:
// The material grid, which comes from texture generation, is needed on both server and client, to generate the tool properties.
TextureGenerator.generate(self);
ToolPhysics.evaluateTool(self);
try TextureGenerator.generate(self);
try ToolPhysics.evaluateTool(self);
return self;
}
pub fn initFromJson(allocator: Allocator, jsonObject: JsonElement) !*Tool {
var self = try initFromCraftingGrid(allocator, extractItemsFromJson(jsonObject.getChild("grid")), jsonObject.get(u32, "seed", 0));
self.durability = jsonObject.get(i32, "durability", self.maxDurability);
pub fn initFromJson(jsonObject: JsonElement) !*Tool {
var self = try initFromCraftingGrid(extractItemsFromJson(jsonObject.getChild("grid")), jsonObject.get(u32, "seed", 0));
self.durability = jsonObject.get(u32, "durability", self.maxDurability);
return self;
}
@ -920,7 +930,7 @@ const Tool = struct {
return items;
}
pub fn save(self: *Tool, allocator: Allocator) !JsonElement {
pub fn save(self: *const Tool, allocator: Allocator) !JsonElement {
var jsonObject = try JsonElement.initObject(allocator);
var jsonArray = try JsonElement.initArray(allocator);
for(self.craftingGrid) |nullItem| {
@ -964,23 +974,28 @@ const Tool = struct {
}
};
pub const Item = union(u8) {
pub const Item = union(enum) {
baseItem: *const BaseItem,
tool: *const Tool,
pub fn init(self: *Item, allocator: Allocator) !void {
_ = allocator;
_ = self;
pub fn init(jsonObject: JsonElement) !Item {
if(reverseIndices.get(jsonObject.get([]const u8, "item", "null"))) |baseItem| {
std.debug.print("{*}", .{baseItem.id.ptr});
return Item{.baseItem = baseItem};
} else {
var toolJson = jsonObject.getChild("tool");
if(toolJson != .JsonObject) return error.ItemNotFound;
return Item{.tool = try Tool.initFromJson(toolJson)};
}
}
pub fn deinit(self: Item, allocator: Allocator) void {
pub fn deinit(self: Item) void {
switch(self) {
.baseItem => {
},
.tool => |_tool| {
_tool.deinit(allocator);
_tool.deinit();
},
}
}
@ -1002,7 +1017,7 @@ pub const Item = union(u8) {
try jsonObject.put("item", _baseItem.id);
},
.tool => |_tool| {
json.put("tool", _tool.toJson(allocator));
try jsonObject.put("tool", try _tool.save(allocator));
},
}
}
@ -1020,27 +1035,28 @@ pub const ItemStack = struct {
supplier.clear();
}
pub fn filled(self: *ItemStack) bool {
pub fn filled(self: *const ItemStack) bool {
if(self.item) |item| {
return self.amount >= item.stackSize();
}
return false;
}
pub fn empty(self: *ItemStack) bool {
pub fn empty(self: *const ItemStack) bool {
return self.amount == 0;
}
/// Returns the number of items actually added/removed.
pub fn add(self: *ItemStack, number: i32) i32 {
std.debug.assert(self.item);
const newAmount = self.amount + number;
var returnValue: i32 = 0;
pub fn add(self: *ItemStack, number: anytype) @TypeOf(number) {
std.debug.assert(self.item != null);
var newAmount = self.amount + number;
var returnValue = number;
if(newAmount < 0) {
returnValue = number - newAmount;
newAmount = 0;
returnValue = newAmount - self.amount;
} else if(newAmount > self.item.?.stackSize()) {
returnValue = number - newAmount + self.item.?.stackSize();
newAmount = self.item.?.stackSize();
returnValue = newAmount - self.amount;
}
self.amount = @intCast(u16, newAmount);
if(self.empty()) {
@ -1050,7 +1066,7 @@ pub const ItemStack = struct {
}
/// whether the given number of items can be added to this stack.
pub fn canAddAll(self: *ItemStack, number: u16) bool {
pub fn canAddAll(self: *const ItemStack, number: u16) bool {
std.debug.assert(self.item);
return @as(u32, self.amount) + number <= self.item.?.stackSize();
}
@ -1063,11 +1079,11 @@ pub const ItemStack = struct {
self.amount = 0;
}
pub fn store(self: *ItemStack, allocator: Allocator) !JsonElement {
pub fn store(self: *const ItemStack, allocator: Allocator) !JsonElement {
var result = try JsonElement.initObject(allocator);
if(self.item) |item| {
item.insertToJson(allocator, result);
result.put("amount", self.amount);
try item.insertIntoJson(allocator, result);
try result.put("amount", self.amount);
}
return result;
}
@ -1085,27 +1101,123 @@ pub const ItemStack = struct {
// }
};
pub const Inventory = struct {
items: []ItemStack,
pub fn init(allocator: Allocator, size: usize) !Inventory {
const self = Inventory{
.items = try allocator.alloc(ItemStack, size),
};
for(self.items) |*item| {
item.* = ItemStack{};
}
return self;
}
pub fn deinit(self: Inventory, allocator: Allocator) void {
for(self.items) |*item| {
item.clear();
}
allocator.free(self.items);
}
/// Returns the amount of items that didn't fit in the inventory.
pub fn addItem(self: Inventory, item: Item, _amount: u16) u16 {
var amount = _amount;
for(self.items) |*stack| {
if(!stack.empty() and std.meta.eql(stack.item, item) and !stack.filled()) {
amount -= stack.add(amount);
if(amount == 0) return 0;
}
}
for(self.items) |*stack| {
if(stack.empty()) {
stack.item = item;
amount -= stack.add(amount);
if(amount == 0) return 0;
}
}
return amount;
}
pub fn canCollect(self: Inventory, item: Item) bool {
for(self.items) |*stack| {
if(stack.empty()) return true;
if(stack.item == item and !stack.filled()) {
return true;
}
}
return false;
}
// TODO: Check if/how this is needed:
// public int getBlock(int slot) {
// return items[slot].getBlock();
// }
pub fn getItem(self: Inventory, slot: usize) ?Item {
return self.items[slot].item;
}
pub fn getStack(self: Inventory, slot: usize) *ItemStack {
return &self.items[slot];
}
pub fn getAmount(self: Inventory, slot: usize) u16 {
return self.items[slot].amount;
}
pub fn save(self: Inventory, allocator: Allocator) !JsonElement {
var jsonObject = try JsonElement.initObject(allocator);
try jsonObject.put("capacity", self.items.len);
for(self.items) |stack, i| {
if(!stack.empty()) {
var buf: [1024]u8 = undefined;
try jsonObject.put(buf[0..std.fmt.formatIntBuf(&buf, i, 10, .lower, .{})], try stack.store(allocator));
}
}
return jsonObject;
}
pub fn loadFromJson(self: Inventory, allocator: Allocator, jsonObject: JsonElement) void {
for(self.items) |*stack, i| {
stack.clear();
var buf: [1024]u8 = undefined;
var stackJson = jsonObject.getChild(buf[0..std.fmt.formatIntBuf(buf, i, 10, .lower, .{})]);
if(stackJson == .JsonObject) {
stack.item = try Item.init(allocator, jsonObject);
stack.amount = stackJson.get(u16, "amount", 0);
}
}
}
};
var arena: std.heap.ArenaAllocator = undefined;
var reverseIndices: std.StringHashMap(*BaseItem) = undefined;
var itemList: std.ArrayList(BaseItem) = undefined;
var itemList: [65536]BaseItem = undefined;
var itemListSize: u16 = 0;
pub fn globalInit() void {
arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
reverseIndices = std.StringHashMap(*BaseItem).init(arena.allocator());
itemList = std.ArrayList(BaseItem).init(arena.allocator());
itemListSize = 0;
}
pub fn register(_: []const u8, id: []const u8, jsonObject: JsonElement) !void {
std.log.info("{s}", .{id});
if(reverseIndices.contains(id)) {
std.log.warn("Registered block with id {s} twice!", .{id});
}
try (try itemList.addOne()).init(arena.allocator(), id, jsonObject);
var newItem = &itemList[itemListSize];
try newItem.init(arena.allocator(), id, jsonObject);
try reverseIndices.put(newItem.id, newItem);
itemListSize += 1;
}
pub fn reset() void {
reverseIndices.clearAndFree();
itemList.clearAndFree();
itemListSize = 0;
// TODO: Use arena.reset() instead.
arena.deinit();
arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
@ -1113,6 +1225,5 @@ pub fn reset() void {
pub fn deinit() void {
reverseIndices.clearAndFree();
itemList.clearAndFree();
arena.deinit();
}

View File

@ -136,7 +136,7 @@ pub const JsonElement = union(JsonType) {
.Null => return JsonElement{.JsonNull={}},
.Bool => return JsonElement{.JsonBool=value},
.Int, .ComptimeInt => return JsonElement{.JsonInt=@intCast(i64, value)},
.Float, .ComptimeFloat => return JsonElement{.JsonInt=@floatCast(f64, value)},
.Float, .ComptimeFloat => return JsonElement{.JsonFloat=@floatCast(f64, value)},
.Union => {
if(@TypeOf(value) == JsonElement) {
return value;
@ -144,6 +144,14 @@ pub const JsonElement = union(JsonType) {
@compileError("Unknown value type.");
}
},
.Pointer => |ptr| {
if(ptr.child == u8 and ptr.size == .Slice) {
std.log.info("String: {s}", .{value});
return JsonElement{.JsonString=value};
} else {
@compileError("Unknown value type.");
}
},
else => {
@compileError("Unknown value type.");
},
@ -152,7 +160,7 @@ pub const JsonElement = union(JsonType) {
pub fn put(self: *const JsonElement, key: []const u8, value: anytype) !void {
const result = createElementFromRandomType(value);
try self.JsonObject.put(key, result);
try self.JsonObject.put(try self.JsonObject.allocator.dupe(u8, key), result);
}
pub fn free(self: *const JsonElement, allocator: Allocator) void {
@ -185,7 +193,6 @@ pub const JsonElement = union(JsonType) {
return self.* == JsonType.JsonNull;
}
// TODO: toString()
fn escape(string: []const u8, allocator: Allocator) ![]const u8 {
var out = std.ArrayList(u8).init(allocator);
for(string) |char| {

View File

@ -22,6 +22,7 @@ pub const c = @cImport ({
});
pub threadlocal var threadAllocator: std.mem.Allocator = undefined;
pub var globalAllocator: std.mem.Allocator = undefined;
pub var threadPool: utils.ThreadPool = undefined;
var logFile: std.fs.File = undefined;
@ -218,6 +219,11 @@ pub fn main() !void {
defer if(gpa.deinit()) {
@panic("Memory leak");
};
var global_gpa = std.heap.GeneralPurposeAllocator(.{.thread_safe=true}){};
globalAllocator = global_gpa.allocator();
defer if(global_gpa.deinit()) {
@panic("Memory leak");
};
// init logging.
try std.fs.cwd().makePath("logs");

View File

@ -5,15 +5,19 @@ const assets = @import("assets.zig");
const Block = @import("blocks.zig").Block;
const chunk = @import("chunk.zig");
const entity = @import("entity.zig");
const items = @import("items.zig");
const Inventory = items.Inventory;
const ItemStack = items.ItemStack;
const json = @import("json.zig");
const main = @import("main.zig");
const game = @import("game.zig");
const settings = @import("settings.zig");
const json = @import("json.zig");
const JsonElement = json.JsonElement;
const renderer = @import("renderer.zig");
const utils = @import("utils.zig");
const vec = @import("vec.zig");
const Vec3d = vec.Vec3d;
const Vec3f = vec.Vec3f;
//TODO: Might want to use SSL or something similar to encode the message
@ -977,7 +981,7 @@ pub const Protocols: struct {
const type_itemStackDrop: u8 = 6;
const type_itemStackCollect: u8 = 7;
const type_timeAndBiome: u8 = 8;
fn receive(_: *Connection, data: []const u8) !void {
fn receive(conn: *Connection, data: []const u8) !void {
switch(data[0]) {
type_renderDistance => {
const renderDistance = std.mem.readIntBig(i32, data[1..5]);
@ -1047,18 +1051,18 @@ pub const Protocols: struct {
// );
},
type_itemStackCollect => {
// TODO:
// JsonObject json = JsonParser.parseObjectFromString(new String(data, offset + 1, length - 1, StandardCharsets.UTF_8));
// Item item = Item.load(json, Cubyz.world.registries);
// if (item == null) {
// break;
// }
// int remaining = Cubyz.player.getInventory_AND_DONT_FORGET_TO_SEND_CHANGES_TO_THE_SERVER().addItem(item, json.getInt("amount", 1));
// sendInventory_full(Cubyz.world.serverConnection, Cubyz.player.getInventory_AND_DONT_FORGET_TO_SEND_CHANGES_TO_THE_SERVER());
// if(remaining != 0) {
// // Couldn't collect everything drop it again.
// itemStackDrop(Cubyz.world.serverConnection, new ItemStack(item, remaining), Cubyz.player.getPosition(), Camera.getDirection(), 0);
// }
const jsonObject = json.parseFromString(main.threadAllocator, data[1..]);
defer jsonObject.free(main.threadAllocator);
const item = try items.Item.init(jsonObject);
game.Player.mutex.lock();
defer game.Player.mutex.unlock();
const remaining = game.Player.inventory__SEND_CHANGES_TO_SERVER.addItem(item, jsonObject.get(u16, "amount", 0));
try sendInventory_full(conn, game.Player.inventory__SEND_CHANGES_TO_SERVER);
if(remaining != 0) {
// Couldn't collect everything drop it again.
try itemStackDrop(conn, ItemStack{.item=item, .amount=remaining}, game.Player.getPosBlocking(), Vec3f{0, 0, 0}, 0);
}
},
type_timeAndBiome => {
if(game.world) |world| {
@ -1134,10 +1138,14 @@ pub const Protocols: struct {
try conn.sendImportant(id, &data);
}
// TODO:
// public void sendInventory_full(ServerConnection conn, Inventory inv) {
// addHeaderAndSendImportant(conn, INVENTORY_FULL, inv.save().toString().getBytes(StandardCharsets.UTF_8));
// }
pub fn sendInventory_full(conn: *Connection, inv: Inventory) !void {
const jsonObject = try inv.save(main.threadAllocator);
defer jsonObject.free(main.threadAllocator);
const string = try jsonObject.toString(main.threadAllocator);
defer main.threadAllocator.free(string);
try addHeaderAndSendImportant(conn, type_inventoryFull, string);
}
pub fn clearInventory(conn: *Connection) !void {
var data: [1]u8 = undefined;
@ -1145,23 +1153,28 @@ pub const Protocols: struct {
try conn.sendImportant(id, &data);
}
// TODO:
// public void itemStackDrop(ServerConnection conn, ItemStack stack, Vector3d pos, Vector3f dir, float vel) {
// JsonObject json = stack.store();
// json.put("x", pos.x);
// json.put("y", pos.y);
// json.put("z", pos.z);
// json.put("dirX", dir.x);
// json.put("dirY", dir.y);
// json.put("dirZ", dir.z);
// json.put("vel", vel);
// addHeaderAndSendImportant(conn, ITEM_STACK_DROP, json.toString().getBytes(StandardCharsets.UTF_8));
// }
pub fn itemStackDrop(conn: *Connection, stack: ItemStack, pos: Vec3d, dir: Vec3f, vel: f32) !void {
var jsonObject = try stack.store(main.threadAllocator);
defer jsonObject.free(main.threadAllocator);
try jsonObject.put("x", pos[0]);
try jsonObject.put("y", pos[1]);
try jsonObject.put("z", pos[2]);
try jsonObject.put("dirX", dir[0]);
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);
try addHeaderAndSendImportant(conn, type_itemStackDrop, string);
}
// TODO:
// public void itemStackCollect(User user, ItemStack stack) {
// addHeaderAndSendImportant(user, ITEM_STACK_COLLECT, stack.store().toString().getBytes(StandardCharsets.UTF_8));
// }
pub fn itemStackCollect(conn: *Connection, stack: ItemStack) !void {
var jsonObject = try stack.store(main.threadAllocator);
defer jsonObject.free(main.threadAllocator);
const string = try jsonObject.toString(main.threadAllocator);
defer main.threadAllocator.free(string);
try addHeaderAndSendImportant(conn, type_itemStackCollect, string);
}
// TODO:
// public void sendTimeAndBiome(User user, ServerWorld world) {

View File

@ -10,16 +10,16 @@ pub fn scrambleSeed(seed: *u64) void {
seed.* = (seed.* ^ multiplier) & mask;
}
fn nextWithBitSize(T: type, seed: *u64, bitSize: u6) T {
fn nextWithBitSize(comptime T: type, seed: *u64, bitSize: u6) T {
seed.* = ((seed.*)*multiplier + addend) & mask;
return @intCast(T, (seed >> (48 - bitSize)) & std.math.maxInt(T));
return @intCast(T, (seed.* >> (48 - bitSize)) & std.math.maxInt(T));
}
fn next(T: type, seed: *u64) T {
nextWithBitSize(T, seed, @bitSizeOf(T));
fn next(comptime T: type, seed: *u64) T {
return nextWithBitSize(T, seed, @bitSizeOf(T));
}
pub fn nextInt(T: type, seed: *u64) T {
pub fn nextInt(comptime T: type, seed: *u64) T {
if(@bitSizeOf(T) > 48) {
@compileError("Did not yet implement support for bigger numbers.");
} else {
@ -27,8 +27,8 @@ pub fn nextInt(T: type, seed: *u64) T {
}
}
pub fn nextIntBounded(T: type, seed: *u64, bound: T) T {
var bitSize = std.math.log2_int_ceil(bound);
pub fn nextIntBounded(comptime T: type, seed: *u64, bound: T) T {
var bitSize = std.math.log2_int_ceil(T, bound);
var result = nextWithBitSize(T, seed, bitSize);
while(result >= bound) {
result = nextWithBitSize(T, seed, bitSize);

View File

@ -877,7 +877,7 @@ pub const RenderStructure = struct {
pub fn getBlock(x: chunk.ChunkCoordinate, y: chunk.ChunkCoordinate, z: chunk.ChunkCoordinate) ?blocks.Block {
const node = RenderStructure._getNode(.{.wx = x, .wy = y, .wz = z, .voxelSize=1}) orelse return null;
const block = (node.mesh.chunk orelse return null).getBlock(x & chunk.chunkMask, y & chunk.chunkMask, z & chunk.chunkMask);
const block = (node.mesh.chunk.load(.Monotonic) orelse return null).getBlock(x & chunk.chunkMask, y & chunk.chunkMask, z & chunk.chunkMask);
return block;
}

View File

@ -22,8 +22,8 @@ pub fn length(self: anytype) @typeInfo(@TypeOf(self)).Vector.child {
return @sqrt(@reduce(.Add, self*self));
}
pub fn normalize(self: anytype) @typeInfo(@TypeOf(self)).Vector.child {
return self/length(self);
pub fn normalize(self: anytype) @TypeOf(self) {
return self/@splat(@typeInfo(@TypeOf(self)).Vector.len, length(self));
}
pub fn cross(self: anytype, other: @TypeOf(self)) @TypeOf(self) {