mirror of
https://github.com/PixelGuys/Cubyz.git
synced 2025-08-03 03:06:55 -04:00
Add random tick events (#1338)
This pull request adds random tick events for blocks. Tick events can be added by using the `tickEvent` trait and contain the `name` of the callback function and the probability ( `chance`). Resolves: https://github.com/PixelGuys/Cubyz/issues/77 --------- Co-authored-by: IntegratedQuantum <43880493+IntegratedQuantum@users.noreply.github.com>
This commit is contained in:
parent
2f48161712
commit
0e1aa68174
@ -2,8 +2,11 @@ const std = @import("std");
|
||||
|
||||
const main = @import("main");
|
||||
const Tag = main.Tag;
|
||||
const utils = main.utils;
|
||||
const ZonElement = @import("zon.zig").ZonElement;
|
||||
const Neighbor = @import("chunk.zig").Neighbor;
|
||||
const chunk = @import("chunk.zig");
|
||||
const Neighbor = chunk.Neighbor;
|
||||
const Chunk = chunk.Chunk;
|
||||
const graphics = @import("graphics.zig");
|
||||
const SSBO = graphics.SSBO;
|
||||
const Image = graphics.Image;
|
||||
@ -76,6 +79,7 @@ var _opaqueVariant: [maxBlockCount]u16 = undefined;
|
||||
var _friction: [maxBlockCount]f32 = undefined;
|
||||
|
||||
var _allowOres: [maxBlockCount]bool = undefined;
|
||||
var _tickEvent: [maxBlockCount]?TickEvent = undefined;
|
||||
var _touchFunction: [maxBlockCount]?*const TouchFunction = undefined;
|
||||
var _blockEntity: [maxBlockCount]?*BlockEntityType = undefined;
|
||||
|
||||
@ -124,7 +128,16 @@ pub fn register(_: []const u8, id: []const u8, zon: ZonElement) u16 {
|
||||
_hasBackFace[size] = zon.get(bool, "hasBackFace", false);
|
||||
_friction[size] = zon.get(f32, "friction", 20);
|
||||
_allowOres[size] = zon.get(bool, "allowOres", false);
|
||||
_touchFunction[size] = TouchFunctions.getFunctionPointer(zon.get([]const u8, "touchFunction", ""));
|
||||
_tickEvent[size] = TickEvent.loadFromZon(zon.getChild("tickEvent"));
|
||||
|
||||
_touchFunction[size] = if(zon.get(?[]const u8, "touchFunction", null)) |touchFunctionName| blk: {
|
||||
const _function = touchFunctions.getFunctionPointer(touchFunctionName);
|
||||
if(_function == null) {
|
||||
std.log.err("Could not find TouchFunction {s}!", .{touchFunctionName});
|
||||
}
|
||||
break :blk _function;
|
||||
} else null;
|
||||
|
||||
_blockEntity[size] = block_entity.getByID(zon.get(?[]const u8, "blockEntity", null));
|
||||
|
||||
const oreProperties = zon.getChild("ore");
|
||||
@ -382,6 +395,10 @@ pub const Block = packed struct { // MARK: Block
|
||||
return _allowOres[self.typ];
|
||||
}
|
||||
|
||||
pub inline fn tickEvent(self: Block) ?TickEvent {
|
||||
return _tickEvent[self.typ];
|
||||
}
|
||||
|
||||
pub inline fn touchFunction(self: Block) ?*const TouchFunction {
|
||||
return _touchFunction[self.typ];
|
||||
}
|
||||
@ -395,35 +412,49 @@ pub const Block = packed struct { // MARK: Block
|
||||
}
|
||||
};
|
||||
|
||||
pub const TouchFunction = fn(block: Block, entity: Entity, posX: i32, posY: i32, posZ: i32, isEntityInside: bool) void;
|
||||
// MARK: Tick
|
||||
pub var tickFunctions: utils.NamedCallbacks(TickFunctions, TickFunction) = undefined;
|
||||
pub const TickFunction = fn(block: Block, _chunk: *chunk.ServerChunk, x: i32, y: i32, z: i32) void;
|
||||
pub const TickFunctions = struct {
|
||||
pub fn replaceWithCobble(block: Block, _chunk: *chunk.ServerChunk, x: i32, y: i32, z: i32) void {
|
||||
std.log.debug("Replace with cobblestone at ({d},{d},{d})", .{x, y, z});
|
||||
const cobblestone = parseBlock("cubyz:cobblestone");
|
||||
|
||||
pub const TouchFunctions = struct {
|
||||
var hashMap: std.StringHashMap(*const TouchFunction) = undefined;
|
||||
const wx = _chunk.super.pos.wx + x;
|
||||
const wy = _chunk.super.pos.wy + y;
|
||||
const wz = _chunk.super.pos.wz + z;
|
||||
|
||||
pub fn init() void {
|
||||
hashMap = .init(main.globalAllocator.allocator);
|
||||
inline for(@typeInfo(TouchFunctions).@"struct".decls) |declaration| {
|
||||
if(@TypeOf(@field(TouchFunctions, declaration.name)) == TouchFunction) {
|
||||
hashMap.putNoClobber(declaration.name, &@field(TouchFunctions, declaration.name)) catch unreachable;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn deinit() void {
|
||||
hashMap.deinit();
|
||||
}
|
||||
|
||||
pub fn getFunctionPointer(id: []const u8) ?*const TouchFunction {
|
||||
const pointer = hashMap.getPtr(id);
|
||||
if(pointer == null) {
|
||||
if(id.len != 0)
|
||||
std.log.err("Could not find touch function {s}.", .{id});
|
||||
return null;
|
||||
}
|
||||
return pointer.?.*;
|
||||
_ = main.server.world.?.cmpxchgBlock(wx, wy, wz, block, cobblestone);
|
||||
}
|
||||
};
|
||||
|
||||
pub const TickEvent = struct {
|
||||
function: *const TickFunction,
|
||||
chance: f32,
|
||||
|
||||
pub fn loadFromZon(zon: ZonElement) ?TickEvent {
|
||||
const functionName = zon.get(?[]const u8, "name", null) orelse return null;
|
||||
|
||||
const function = tickFunctions.getFunctionPointer(functionName) orelse {
|
||||
std.log.err("Could not find TickFunction {s}.", .{functionName});
|
||||
return null;
|
||||
};
|
||||
|
||||
return TickEvent{.function = function, .chance = zon.get(f32, "chance", 1)};
|
||||
}
|
||||
|
||||
pub fn tryRandomTick(self: *const TickEvent, block: Block, _chunk: *chunk.ServerChunk, x: i32, y: i32, z: i32) void {
|
||||
if(self.chance >= 1.0 or main.random.nextFloat(&main.seed) < self.chance) {
|
||||
self.function(block, _chunk, x, y, z);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// MARK: Touch
|
||||
pub var touchFunctions: utils.NamedCallbacks(TouchFunctions, TouchFunction) = undefined;
|
||||
pub const TouchFunction = fn(block: Block, entity: Entity, posX: i32, posY: i32, posZ: i32, isEntityInside: bool) void;
|
||||
pub const TouchFunctions = struct {};
|
||||
|
||||
pub const meshes = struct { // MARK: meshes
|
||||
const AnimationData = extern struct {
|
||||
startFrame: u32,
|
||||
|
@ -609,8 +609,11 @@ pub fn main() void { // MARK: main()
|
||||
block_entity.init();
|
||||
defer block_entity.deinit();
|
||||
|
||||
blocks.TouchFunctions.init();
|
||||
defer blocks.TouchFunctions.deinit();
|
||||
blocks.tickFunctions = .init();
|
||||
defer blocks.tickFunctions.deinit();
|
||||
|
||||
blocks.touchFunctions = .init();
|
||||
defer blocks.touchFunctions.deinit();
|
||||
|
||||
models.init();
|
||||
defer models.deinit();
|
||||
|
@ -393,6 +393,7 @@ const WorldIO = struct { // MARK: WorldIO
|
||||
self.world.spawn = worldData.get(Vec3i, "spawn", .{0, 0, 0});
|
||||
self.world.biomeChecksum = worldData.get(i64, "biomeChecksum", 0);
|
||||
self.world.name = main.globalAllocator.dupe(u8, worldData.get([]const u8, "name", self.world.path));
|
||||
self.world.tickSpeed = worldData.get(u32, "tickSpeed", 12);
|
||||
}
|
||||
|
||||
pub fn saveWorldData(self: WorldIO) !void {
|
||||
@ -406,6 +407,7 @@ const WorldIO = struct { // MARK: WorldIO
|
||||
worldData.put("biomeChecksum", self.world.biomeChecksum);
|
||||
worldData.put("name", self.world.name);
|
||||
worldData.put("lastUsedTime", std.time.milliTimestamp());
|
||||
worldData.put("tickSpeed", self.world.tickSpeed);
|
||||
// TODO: Save entities
|
||||
try self.dir.writeZon("world.zig.zon", worldData);
|
||||
}
|
||||
@ -426,6 +428,8 @@ pub const ServerWorld = struct { // MARK: ServerWorld
|
||||
lastUnimportantDataSent: i64,
|
||||
doGameTimeCycle: bool = true,
|
||||
|
||||
tickSpeed: u32 = 12,
|
||||
|
||||
defaultGamemode: main.game.Gamemode = undefined,
|
||||
allowCheats: bool = undefined,
|
||||
|
||||
@ -920,6 +924,42 @@ pub const ServerWorld = struct { // MARK: ServerWorld
|
||||
self.dropWithCooldown(stack, pos, dir, velocity, 0);
|
||||
}
|
||||
|
||||
fn tickBlocksInChunk(self: *ServerWorld, _chunk: *chunk.ServerChunk) void {
|
||||
for(0..self.tickSpeed) |_| {
|
||||
const blockIndex: i32 = main.random.nextInt(i32, &main.seed);
|
||||
|
||||
const x: i32 = blockIndex >> chunk.chunkShift2 & chunk.chunkMask;
|
||||
const y: i32 = blockIndex >> chunk.chunkShift & chunk.chunkMask;
|
||||
const z: i32 = blockIndex & chunk.chunkMask;
|
||||
|
||||
_chunk.mutex.lock();
|
||||
const block = _chunk.getBlock(x, y, z);
|
||||
_chunk.mutex.unlock();
|
||||
if(block.tickEvent()) |event| {
|
||||
event.tryRandomTick(block, _chunk, x, y, z);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn tick(self: *ServerWorld) void {
|
||||
ChunkManager.mutex.lock();
|
||||
var iter = ChunkManager.entityChunkHashMap.valueIterator();
|
||||
var currentChunks: main.ListUnmanaged(*EntityChunk) = .initCapacity(main.stackAllocator, iter.len);
|
||||
defer currentChunks.deinit(main.stackAllocator);
|
||||
while(iter.next()) |entityChunk| {
|
||||
entityChunk.*.increaseRefCount();
|
||||
currentChunks.append(main.stackAllocator, entityChunk.*);
|
||||
}
|
||||
ChunkManager.mutex.unlock();
|
||||
|
||||
// tick blocks
|
||||
for(currentChunks.items) |entityChunk| {
|
||||
defer entityChunk.decreaseRefCount();
|
||||
const ch = entityChunk.getChunk() orelse continue;
|
||||
self.tickBlocksInChunk(ch);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update(self: *ServerWorld) void { // MARK: update()
|
||||
const newTime = std.time.milliTimestamp();
|
||||
var deltaTime = @as(f32, @floatFromInt(newTime - self.lastUpdateTime))/1000.0;
|
||||
@ -941,6 +981,7 @@ pub const ServerWorld = struct { // MARK: ServerWorld
|
||||
main.network.Protocols.genericUpdate.sendTime(user.conn, self);
|
||||
}
|
||||
}
|
||||
self.tick();
|
||||
// TODO: Entities
|
||||
|
||||
// Item Entities
|
||||
|
@ -2095,3 +2095,54 @@ fn CastFunctionReturnToAnyopaqueType(Fn: type) type {
|
||||
pub fn castFunctionReturnToAnyopaque(function: anytype) *const CastFunctionReturnToAnyopaqueType(@TypeOf(function)) {
|
||||
return @ptrCast(&function);
|
||||
}
|
||||
|
||||
// MARK: Callback
|
||||
pub fn NamedCallbacks(comptime Child: type, comptime Function: type) type {
|
||||
return struct {
|
||||
const Self = @This();
|
||||
|
||||
hashMap: std.StringHashMap(*const Function) = undefined,
|
||||
|
||||
pub fn init() Self {
|
||||
var self = Self{.hashMap = .init(main.globalAllocator.allocator)};
|
||||
inline for(@typeInfo(Child).@"struct".decls) |declaration| {
|
||||
if(@TypeOf(@field(Child, declaration.name)) == Function) {
|
||||
std.log.debug("Registered Callback '{s}'", .{declaration.name});
|
||||
self.hashMap.putNoClobber(declaration.name, &@field(Child, declaration.name)) catch unreachable;
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Self) void {
|
||||
self.hashMap.deinit();
|
||||
}
|
||||
|
||||
pub fn getFunctionPointer(self: *Self, id: []const u8) ?*const Function {
|
||||
return self.hashMap.get(id);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
test "NamedCallbacks registers functions" {
|
||||
const TestFunction = fn(_: i32) void;
|
||||
const TestFunctions = struct {
|
||||
// Callback should register this
|
||||
pub fn testFunction(_: i32) void {}
|
||||
pub fn otherTestFunction(_: i32) void {}
|
||||
// Callback should ignore this
|
||||
pub fn wrongSignatureFunction(_: i32, _: bool) void {}
|
||||
};
|
||||
var testFunctions: NamedCallbacks(TestFunctions, TestFunction) = undefined;
|
||||
|
||||
testFunctions = .init();
|
||||
defer testFunctions.deinit();
|
||||
|
||||
try std.testing.expectEqual(2, testFunctions.hashMap.count());
|
||||
|
||||
try std.testing.expectEqual(&TestFunctions.testFunction, testFunctions.getFunctionPointer("testFunction").?);
|
||||
try std.testing.expectEqual(&TestFunctions.otherTestFunction, testFunctions.getFunctionPointer("otherTestFunction").?);
|
||||
|
||||
try std.testing.expectEqual(null, testFunctions.getFunctionPointer("functionTest"));
|
||||
try std.testing.expectEqual(null, testFunctions.getFunctionPointer("wrongSignatureFunction"));
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user