diff --git a/build.zig b/build.zig index 11f6320..372552a 100644 --- a/build.zig +++ b/build.zig @@ -27,6 +27,7 @@ const engineDepList = [_][]const u8{ "assets", "audio", "core", + "net", "papyrus", "platform", "rend", diff --git a/build.zig.zon b/build.zig.zon index 764fb41..f1e8a8f 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -5,6 +5,7 @@ .assets = .{ .path = "engine/assets" }, .audio = .{ .path = "engine/audio" }, .core = .{ .path = "engine/core" }, + .net = .{ .path = "engine/net" }, .papyrus = .{ .path = "engine/papyrus" }, .physics = .{ .path = "engine/physics" }, .platform = .{ .path = "engine/platform" }, diff --git a/engine/backlog.zig b/engine/backlog.zig index 0074673..2298c6e 100644 --- a/engine/backlog.zig +++ b/engine/backlog.zig @@ -13,6 +13,7 @@ pub const platform = @import("platform").module; pub const assets = @import("assets").module; pub const rend = @import("rend").module; pub const audio = @import("audio").module; +pub const net = @import("net").module; pub const ui = @import("ui").module; pub const papyrus = @import("papyrus").module; pub const physics = @import("physics").module; diff --git a/engine/core/src/core.zig b/engine/core/src/core.zig index 169016f..bf41c37 100644 --- a/engine/core/src/core.zig +++ b/engine/core/src/core.zig @@ -53,6 +53,7 @@ pub usingnamespace @import("file_dialogue.zig"); pub const panickers = @import("panickers.zig"); pub const scene = @import("scene.zig"); +pub const ScenePosRot = scene.ScenePosRot; pub const SceneSystem = scene.SceneSystem; pub const Engine = engine.Engine; @@ -118,8 +119,18 @@ pub const walkAndPrintStack = stacks.walkAndPrintStack; pub const gameObject = @import("gameObject.zig"); +pub const SpawnParameters = gameObject.SpawnParameters; pub const GameObject = gameObject.GameObject; pub const GameObjectSystem = gameObject.GameObjectSystem; +pub const MessageInfo = gameObject.MessageInfo; + +pub fn registerObject(comptime T: type, name: []const u8) !void { + try get(GameObjectSystem).registerObject(name, T); +} + +pub fn registerObjectAdvanced(comptime T: type, name: []const u8, comptime funcName: []const u8) !void { + try get(GameObjectSystem).registerObjectAdvanced(name, T, funcName); +} pub fn fs() *PackerFS { return gPackerFS; @@ -215,6 +226,8 @@ pub fn start_module_(map: *SpecVariantMap, args: anytype, allocator: std.mem.All try algorithm.string_pool.setup(allocator); _ = try gEngine.createObject(script_bindings.ScriptTicks, .{ .can_tick = true }); + + _ = try createObject(GameObjectSystem, .{ .can_tick = true }); _ = try inputs.initInputStack(); // components define @@ -384,6 +397,10 @@ pub fn getEngineObject(comptime T: type) ?*T { return null; } +pub fn get(comptime T: type) *T { + return EngineObject(T).get(); +} + pub fn loadModule(moduleName: []const u8, watch: bool) !void { if (watch == false) unreachable; // not implemented diff --git a/engine/core/src/ecs.zig b/engine/core/src/ecs.zig index cde16b3..eadd02e 100644 --- a/engine/core/src/ecs.zig +++ b/engine/core/src/ecs.zig @@ -125,7 +125,7 @@ pub fn destroyEntity(e: Entity) void { entityEntry.containers.deinit(registry.allocator); registry.baseSet.destroyObject(e.handle); } else { - core.engine_log("UNABLE TO DESTROY ENTITY {d}", .{e.handle.index}); + // core.engine_log("UNABLE TO DESTROY ENTITY {d}", .{e.handle.index}); } } @@ -147,7 +147,7 @@ pub fn CreateEntity_Lua(state: lua.LuaState) i32 { pub fn setup(allocator: std.mem.Allocator) !void { try ComponentRef.setupFormatBuffer(allocator); - _ = try core.createObject(EcsRegistry, .{ .can_tick = true }); + _ = try core.createObject(EcsRegistry, .{ .can_tick = true, .isCore = true }); } pub fn shutdown() void { @@ -349,8 +349,10 @@ pub const Entity = struct { }, }; - pub fn destroy(self: *@This()) void { - destroyEntity(self.*); + pub fn destroy(self: @This()) void { + //if (core.getEngineObject(EcsRegistry) != null) { + destroyEntity(self); + // } } pub fn fromHandle(handle: core.ObjectHandle) @This() { diff --git a/engine/core/src/gameObject.zig b/engine/core/src/gameObject.zig index 8b8e74b..7cb53f9 100644 --- a/engine/core/src/gameObject.zig +++ b/engine/core/src/gameObject.zig @@ -2,17 +2,29 @@ // // lets really cook a good API here -pub const GameObjectInterfaceVTable = struct { - destroy: *const fn (*anyopaque) void, - tick: ?*const fn (*anyopaque, f64) void, - getEntity: ?*const fn (*anyopaque) core.Entity, - objectBaseName: core.Name = core.DefineName("UnknownObject"), - create: *const fn (allocator: std.mem.Allocator, entity: core.Entity) *@This(), +pub const SpawnObjectResult = struct { ptr: *anyopaque, entity: core.Entity }; +pub const ObjectCreateFn = *const fn (allocator: std.mem.Allocator, entity: core.Entity, parameters: SpawnParameters) ObjectError!*anyopaque; +pub const MessageHandlerFunc = *const fn (*anyopaque, *const MessageInfo, *anyopaque) void; + +pub const GameObjectInterfaceVTable = struct { + destroy: *const fn (*anyopaque, std.mem.Allocator) void, + tick: ?*const fn (*anyopaque, f64) void, + objectTypeName: core.Name = core.DefineName("UnknownType"), + objectBaseName: core.Name = core.DefineName("UnknownObject"), + create: ObjectCreateFn, messageHandlers: std.AutoHashMapUnmanaged(u32, MessageHandlerFunc) = .{}, - pub const MessageHandlerFunc = *const fn (*anyopaque, *const MessageInfo, *anyopaque) void; - // fn onCustomMessage(self, meta: core.MessageInfo, m: *const MyCustomMessage) void + pub fn deinit(self: *@This(), allocator: std.mem.Allocator) void { + self.messageHandlers.deinit(allocator); + } +}; + +pub const ObjectError = error{ + OutOfMemory, + BadInit, + UnknownState, + UnknownObject, }; pub const MessageInfo = struct { @@ -29,53 +41,149 @@ pub const GameObjectRef = struct { pub const GameObject = struct { pub var BaseContainer: *core.SparseSet(GameObject) = undefined; - pub const ComponentName = "core.GameObject"; + pub const ComponentName = "GameObjectComponent"; pub const ScriptExports: []const []const u8 = &.{}; // set by GameObjectSystem when it calls Entity objectRef: GameObjectRef = undefined, + destroyed: bool = false, - pub fn setObjectReference(self: *@This(), table: *GameObjectInterfaceVTable, object: *anyopaque) void { - self.objectRef.table = table; - self.objectRef.ptr = object; + pub fn deinitECS(self: *@This(), handle: core.ObjectHandle) void { + _ = handle; + if (!self.destroyed) { + self.destroyed = true; + self.objectRef.table.destroy(self.objectRef.ptr, core.get(GameObjectSystem).allocator); + } } }; +pub const SpawnParameters = struct { + posRot: core.ScenePosRot = .{}, + scale: core.Vectorf = .{ .z = 1.0, .y = 1.0, .x = 1.0 }, +}; + +pub const SpawnEvent = struct { + interface: GameObjectInterfaceVTable, +}; + pub const GameObjectSystem = struct { // this is the new one, GameObjectList should be deleted after this passes initial usability pub var NeonObjectTable = core.EngineObjectVTable.from(@This(), "core.GameObjectSystem"); allocator: std.mem.Allocator, - objectDefinitions: *core.SparseMap(GameObjectInterfaceVTable), + objectDefinitions: std.AutoHashMapUnmanaged(u32, GameObjectInterfaceVTable) = .{}, + typesArena: std.heap.ArenaAllocator, + + objectSpawnEvents: std.ArrayListUnmanaged(SpawnEvent) = .{}, pub fn create(allocator: std.mem.Allocator) !*@This() { - const self = try allocator.create(*@This()); + const self = try allocator.create(@This()); self.* = .{ .allocator = allocator, - .sparseMap = try core.SparseMap(GameObjectInterfaceVTable).create(allocator), + .typesArena = std.heap.ArenaAllocator.init(self.allocator), }; return self; } - pub fn spawnObjectByType(self: *@This(), comptime T: type) !*T { - _ = self; + pub fn spawnObject( + self: *@This(), + comptime T: type, + objectName: []const u8, + parameters: SpawnParameters, + ) !*T { + var n = core.MakeName(objectName); + return self.spawnObjectByName(T, &n, parameters); + } + + pub fn spawnObjectByName(self: *@This(), comptime T: type, objectName: *core.Name, parameters: SpawnParameters) !*T { + return @ptrCast(@alignCast((try self.spawnObjectFromTableByName(objectName, parameters)).ptr)); + } + + pub fn spawnObjectFromTableByName(self: *@This(), objectName: *core.Name, parameters: SpawnParameters) !SpawnObjectResult { + const interface: *GameObjectInterfaceVTable = self.objectDefinitions.getPtr(objectName.handle()) orelse { + return ObjectError.UnknownObject; + }; + + const entity = try core.createEntity(); + const objectComponent = entity.addComponent(GameObject).?; + const rv = SpawnObjectResult{ .ptr = try interface.create(self.allocator, entity, parameters), .entity = entity }; + + objectComponent.objectRef = .{ + .table = interface, + .ptr = rv.ptr, + }; + + return rv; + } + + // registers an object for spawning with the object name + pub fn registerObject(self: *@This(), objectName: []const u8, comptime T: type) !void { + try self.registerObjectAdvanced(objectName, T, "create"); + } + + pub fn registerObjectAdvanced(self: *@This(), name: []const u8, comptime T: type, comptime createFunctionName: []const u8) !void { + var n = core.MakeName(name); + + const Wrap = struct { + pub fn create(allocator: std.mem.Allocator, entity: core.Entity, parameters: SpawnParameters) ObjectError!*anyopaque { + return @field(T, createFunctionName)(allocator, entity, parameters) catch { + return ObjectError.BadInit; + }; + } + + pub fn destroy(p: *anyopaque, alloc: std.mem.Allocator) void { + T.destroy(@ptrCast(@alignCast(p)), alloc); + } + + pub fn tick(p: *anyopaque, dt: f64) void { + T.tick(@ptrCast(@alignCast(p)), dt); + } + }; + + var interface = GameObjectInterfaceVTable{ + .create = Wrap.create, + .tick = if (@hasDecl(T, "tick")) Wrap.tick else null, + .destroy = Wrap.destroy, + .objectTypeName = core.MakeTypeName(T), + .objectBaseName = n, + .messageHandlers = .{}, + }; + + if (@hasDecl(T, "MessageHandlers")) { + inline for (T.MessageHandlers) |handlerName| { + var messageHandlerName = core.MakeName(handlerName); + // const handlerFunc = @field(T, handlerName); + // const handlerTypeInfo = @typeInfo(@TypeOf(handlerFunc)); + // const M = @typeInfo(handlerTypeInfo.@"fn".params[3].type.?).pointer.child; + + const HandlerWrap = struct { + pub fn messageHandler(p: *anyopaque, info: *const MessageInfo, message: *anyopaque) void { + @field(T, handlerName)(@ptrCast(@alignCast(p)), info, @ptrCast(@alignCast(message))); + } + }; + + try interface.messageHandlers.put(self.typesArena.allocator(), messageHandlerName.handle(), HandlerWrap.messageHandler); + } + } + + try self.objectDefinitions.put(self.typesArena.allocator(), n.handle(), interface); } pub fn tick(self: *@This(), dt: f64) void { // for (self.objects.items) |object| { - //if (object.vtable.tick) |tick_fn| { - //tick_fn(object.ptr, dt); - //} - //} + // if (object.vtable.tick) |tick_fn| { + // tick_fn(object.ptr, dt); + // } + // } _ = self; _ = dt; } pub fn destroy(self: *@This()) void { - self.objects.deinit(); + self.typesArena.deinit(); self.allocator.destroy(self); } }; diff --git a/engine/core/src/scene.zig b/engine/core/src/scene.zig index 5115476..6116d4b 100644 --- a/engine/core/src/scene.zig +++ b/engine/core/src/scene.zig @@ -23,7 +23,7 @@ pub const SceneMobilityMode = enum { moveable, // sceneobject is moveable and has it's final transform updated }; -pub const SceneObjectPosRot = struct { +pub const ScenePosRot = struct { position: core.Vectorf = .{ .x = 0, .y = 0, .z = 0 }, rotation: core.Rotation = core.Rotation.init(), scale: core.Vectorf = core.Vectorf.new(1.0, 1.0, 1.0), @@ -60,7 +60,7 @@ pub const SceneObjectRepr = struct { pub const SceneObject = struct { _repr: SceneObjectRepr = .{}, // not public - posRot: SceneObjectPosRot = .{}, // position and rotation + posRot: ScenePosRot = .{}, // position and rotation settings: SceneObjectSettings = .{}, // children: ArrayListUnmanaged(core.ObjectHandle) = .{}, @@ -137,6 +137,14 @@ pub const Scene = struct { core.engine_log("handle.index = 0x{x} generation = {d} alive={any}", .{ self.handle.index, self.handle.generation, self.handle.alive }); } + pub fn setPosRot(self: @This(), newPosRot: ScenePosRot) void { + if (SceneObjectContainer.get(self.handle, .posRot)) |posRot| { + posRot.* = newPosRot; + } else { + core.engine_log("setposRot failed handle.index = 0x{x} generation = {d} alive={any}", .{ self.handle.index, self.handle.generation, self.handle.alive }); + } + } + pub fn setPosition(self: @This(), position: core.Vectorf) void { if (SceneObjectContainer.get(self.handle, .posRot)) |posRot| { posRot.*.position = position; @@ -145,7 +153,7 @@ pub const Scene = struct { } } - pub fn getPosRot(self: *@This()) *SceneObjectPosRot { + pub fn getPosRot(self: *@This()) *ScenePosRot { return SceneObjectContainer.get(self.handle, .posRot).?; } @@ -214,6 +222,8 @@ pub const Scene = struct { } } + pub fn getTickCount() u32 {} + pub fn getAndResolveTransform(self: @This()) core.Transform { const repr: *SceneObjectRepr = SceneObjectContainer.get(self.handle, ._repr).?; if (repr.lastUpdate != getSceneSystem().tickCount) { @@ -227,6 +237,12 @@ pub const Scene = struct { return SceneObjectContainer.get(self.handle, ._repr).?.transform; } + pub fn updateTransform(self: @This()) void { + const repr = SceneObjectContainer.get(self.handle, ._repr).?; + const posRot = SceneObjectContainer.get(self.handle, .posRot).?; + getSceneSystem().updateTransform(repr, posRot); + } + // you MUST clearTransfomRefUnsafe() before destroying this transform pub fn setTransformRefUnsafe(self: @This(), ref: *core.Transform) void { SceneObjectContainer.get(self.handle, ._repr).?.transformOverride = ref; @@ -288,7 +304,7 @@ pub const SceneSystem = struct { pub const FieldType = SceneObjectSet.FieldType; // internal update transform function - fn updateTransform(self: *@This(), repr: *SceneObjectRepr, posRot: *const SceneObjectPosRot) void { + fn updateTransform(self: *@This(), repr: *SceneObjectRepr, posRot: *const ScenePosRot) void { if (repr.lastUpdate == self.tickCount) { return; } @@ -385,6 +401,7 @@ pub const SceneSystem = struct { pub fn tick(self: *@This(), deltaTime: f64) void { var z = tracy.ZoneNC(@src(), "Scene System Tick", 0xAABBDD); defer z.End(); + self.updateTransforms(); _ = deltaTime; } @@ -392,7 +409,6 @@ pub const SceneSystem = struct { pub fn deinit(self: *@This()) void { self.dynamicObjects.deinit(self.allocator); self.childrenArena.deinit(); - // core.undefineComponent(Scene); Scene.SceneObjectContainer.destroy(); self.allocator.destroy(self); } diff --git a/engine/modulelist.zig b/engine/modulelist.zig index 77956d6..676e967 100644 --- a/engine/modulelist.zig +++ b/engine/modulelist.zig @@ -3,6 +3,7 @@ pub const list = [_][]const u8{ "platform", "assets", "audio", + "net", "physics", "rend", diff --git a/engine/net/build.zig b/engine/net/build.zig new file mode 100644 index 0000000..77b984c --- /dev/null +++ b/engine/net/build.zig @@ -0,0 +1,35 @@ +const std = @import("std"); + +const depList = [_][]const u8{ + "core", +}; + +pub fn build(b: *std.Build) void { + const target = b.standardTargetOptions(.{}); + const optimize = b.standardOptimizeOption(.{}); + const static_build = b.option(bool, "static_build", "builds backlog dependencies for static linking") orelse false; + + const mod = b.addModule("net", .{ + .target = target, + .optimize = optimize, + .root_source_file = b.path("src/net.zig"), + }); + + for (depList) |depName| { + const dep = b.dependency(depName, .{ .target = target, .optimize = optimize, .static_build = static_build }); + + mod.addImport(depName, dep.module(depName)); + } + + const test_step = b.step("test", "run unit tests for net"); + const tests = b.addTest(.{ + .target = target, + .optimize = optimize, + .root_source_file = b.path("tests/tests.zig"), + }); + + tests.root_module.addImport("net", mod); + const runArtifact = b.addRunArtifact(tests); + test_step.dependOn(&runArtifact.step); + b.installArtifact(tests); +} \ No newline at end of file diff --git a/engine/net/build.zig.zon b/engine/net/build.zig.zon new file mode 100644 index 0000000..10a5d03 --- /dev/null +++ b/engine/net/build.zig.zon @@ -0,0 +1,10 @@ +.{ + .name = .net, + .version = "0.0.0", + .dependencies = .{ + .core = .{ .path = "../core" }, + }, + .paths = .{ + "", + }, +} \ No newline at end of file diff --git a/engine/net/src/net.zig b/engine/net/src/net.zig new file mode 100644 index 0000000..b16aef2 --- /dev/null +++ b/engine/net/src/net.zig @@ -0,0 +1,33 @@ +const core = @import("core"); +const std = @import("std"); + +const netEngine = @import("netEngine.zig"); +pub const NetEngine = netEngine.NetEngine; +pub const net_err = netEngine.net_err; +pub const net_errs = netEngine.net_errs; +pub const net_log = netEngine.net_log; +pub const net_logs = netEngine.net_logs; + +pub fn start_module(spec: *core.SpecVariantMap, args: anytype, allocator: std.mem.Allocator) !void { + _ = args; + _ = spec; + _ = allocator; + + if (core.isUtility()) { + return; + } + + _ = core.createObject(NetEngine, .{ .can_tick = true }) catch unreachable; +} + +pub fn shutdown_module(allocator: std.mem.Allocator) void { + core.get(NetEngine).shutdown(); + _ = allocator; +} + +pub const context = core.EngineObject(NetEngine).get; + +pub const Module = core.ModuleDescription{ + .name = "net", + .enabledByDefault = false, +}; diff --git a/engine/net/src/netEngine.zig b/engine/net/src/netEngine.zig new file mode 100644 index 0000000..b846f6f --- /dev/null +++ b/engine/net/src/netEngine.zig @@ -0,0 +1,49 @@ +const core = @import("core"); +const std = @import("std"); + +pub fn net_log(comptime fmt: []const u8, args: anytype) void { + core.printInner("[NET ]: " ++ fmt ++ "\n", args); +} + +pub fn net_logs(comptime fmt: []const u8) void { + core.printInner("[NET ]: " ++ fmt ++ "\n", .{}); +} + +pub fn net_err(comptime fmt: []const u8, args: anytype) void { + core.printInner("[NET ]: ERROR!! " ++ fmt ++ "\n", args); +} + +pub fn net_errs(comptime fmt: []const u8) void { + core.printInner("[NET ]: ERROR!! " ++ fmt ++ "\n", .{}); +} + +pub const NetEngine = struct { + pub var NeonObjectTable: core.EngineObjectVTable = core.EngineObjectVTable.from(@This(), "net.NetEngine"); + + allocator: std.mem.Allocator, + + pub fn init(allocator: std.mem.Allocator) !*@This() { + const self = try allocator.create(@This()); + self.* = @This(){ + .allocator = allocator, + }; + + net_logs("NetEngine initialized"); + return self; + } + + pub fn shutdown(self: *@This()) void { + net_logs("NetEngine shutting down"); + _ = self; + } + + pub fn deinit(self: *@This()) void { + self.allocator.destroy(self); + } + + pub fn tick(self: *@This(), deltaTime: f64) void { + _ = self; + _ = deltaTime; + // Network tick logic will go here + } +}; \ No newline at end of file diff --git a/engine/rend/src/camera/CameraComponent.zig b/engine/rend/src/camera/CameraComponent.zig index 0cef385..7e164e7 100644 --- a/engine/rend/src/camera/CameraComponent.zig +++ b/engine/rend/src/camera/CameraComponent.zig @@ -119,7 +119,7 @@ pub fn resolve(self: *@This()) void { base = mul(core.zm.rotationX(self.pitch), base); base = mul(core.zm.rotationZ(self.roll), base); - var posRot: core.scene.SceneObjectPosRot = .{ + var posRot: core.scene.ScenePosRot = .{ .rotation = scene.getRotation(), .position = scene.getPosition(), }; diff --git a/extras/gameExtras/src/debuggers/objectSystemSpawner.zig b/extras/gameExtras/src/debuggers/objectSystemSpawner.zig new file mode 100644 index 0000000..1ee44bf --- /dev/null +++ b/extras/gameExtras/src/debuggers/objectSystemSpawner.zig @@ -0,0 +1,84 @@ +allocator: std.mem.Allocator, + +selectedObjectToSpawn: ?core.Name = null, + +spawnedObjects: std.ArrayListUnmanaged(core.Entity) = .{}, + +pub var NeonObjectTable: core.EngineObjectVTable = core.EngineObjectVTable.from(@This(), "extras.ObjectSystemSpawner"); + +pub fn create(allocator: std.mem.Allocator) !*@This() { + const self = try allocator.create(@This()); + + self.* = .{ .allocator = allocator }; + + if (core.getEngineObject(igu.TopBar)) |topbar| { + topbar.addMenuObject(self, "New Object Spawner") catch {}; + } + + return self; +} + +// spawns an arbitrary object, no type acquisition here +pub fn spawnObjectAt(self: *@This(), spawnPosRot: core.ScenePosRot) ?core.Entity { + if (self.selectedObjectToSpawn == null) + return null; + + const objectSystem: *core.GameObjectSystem = core.get(core.GameObjectSystem); + const r = objectSystem.spawnObjectFromTableByName(&self.selectedObjectToSpawn.?, .{ .posRot = spawnPosRot }) catch return null; + self.spawnedObjects.append(self.allocator, r.entity) catch return null; + return r.entity; +} + +pub fn windowOpen(entry: *igu.MenuEntry, dt: f64) void { + const self: *@This() = @ptrCast(@alignCast(entry.ctx)); + _ = dt; + + const object = core.getEngineObject(core.GameObjectSystem).?; + + if (ig.begin("ObjectSystemSpawner", null, .{})) { + if (self.selectedObjectToSpawn) |*selected| { + ig.textf("now spawning : {s}", .{selected.utf8()}); + } else { + ig.textf("click an object to spawn", .{}); + } + + var buf: [256]u8 = undefined; + var i = object.objectDefinitions.iterator(); + while (i.next()) |v| { + const x = std.fmt.bufPrintZ(&buf, "spawn##{d}", .{v.value_ptr.objectBaseName.handle()}) catch unreachable; + ig.textf("{s}", .{v.value_ptr.objectBaseName.utf8()}); + ig.sameLine(0, 10); + if (ig.smallButton(x)) { + self.selectedObjectToSpawn = v.value_ptr.objectBaseName; + } + } + + ig.separator(); + var destroyed: ?usize = null; + for (self.spawnedObjects.items, 0..) |entity, j| { + const x = std.fmt.bufPrintZ(&buf, "delete {d}", .{entity.handle.index}) catch unreachable; + if (ig.smallButton(x)) { + entity.destroy(); + destroyed = j; + } + } + + if (destroyed) |destroyedIndex| { + _ = self.spawnedObjects.orderedRemove(destroyedIndex); + } + } + + ig.end(); +} + +pub fn destroy(self: *@This()) void { + self.spawnedObjects.deinit(self.allocator); + self.allocator.destroy(self); +} + +const backlog = @import("Backlog"); +const core = backlog.core; +const rend = backlog.rend; +const ig = backlog.imgui.api; +const igu = backlog.imgui.utils; +const std = @import("std"); diff --git a/extras/gameExtras/src/gameExtras.zig b/extras/gameExtras/src/gameExtras.zig index 0dcd5a6..33ddd16 100644 --- a/extras/gameExtras/src/gameExtras.zig +++ b/extras/gameExtras/src/gameExtras.zig @@ -3,6 +3,7 @@ pub const games = @import("gameplay/games.zig"); pub const inputDebugger = @import("debuggers/inputDebugger.zig"); pub const ObjectSpawner = @import("debuggers/objectSpawner.zig"); +pub const ObjectSystemSpawner = @import("debuggers/objectSystemSpawner.zig"); pub const RendererDebug = @import("debuggers/RendererDebug.zig"); pub const EngineTool = @import("debuggers/EngineTool.zig"); pub const PhysicsObjectList = @import("debuggers/PhysicsObjectList.zig"); @@ -14,8 +15,9 @@ pub fn setup() !void { _ = try core.createObject(games.GameList, .{}); } +pub const GameModeMessage = games.GameModeMessage; +pub const GameModeState = games.GameModeState; pub const getGame = games.getGame; - pub const beginGame = games.beginGame; pub const endGame = games.endGame; pub const unloadGame = games.unloadGame; diff --git a/extras/gameExtras/src/gameplay/games.zig b/extras/gameExtras/src/gameplay/games.zig new file mode 100644 index 0000000..15335e4 --- /dev/null +++ b/extras/gameExtras/src/gameplay/games.zig @@ -0,0 +1,121 @@ +// games are a + +pub const GameInterface = struct { + ptr: *anyopaque, + beginPlayFn: *const fn (*anyopaque) void, + endPlayFn: *const fn (*anyopaque) void, + + gameName: core.Name, + tags: std.ArrayListUnmanaged(core.Name) = .{}, + + pub fn beginPlay(self: @This()) void { + self.beginPlayFn(self.ptr); + } + + pub fn endPlay(self: @This()) void { + self.endPlayFn(self.ptr); + } +}; + +pub const GameList = struct { + pub var NeonObjectTable = core.EngineObjectVTable.from(@This(), "extras.GameList"); + + allocator: std.mem.Allocator, + + games: std.StringHashMapUnmanaged(*GameInterface) = .{}, + gamesByTag: std.AutoHashMapUnmanaged(u32, GameInterfaceList) = .{}, + gamesLinear: GameInterfaceList = .{}, + + const GameInterfaceList = std.ArrayListUnmanaged(*GameInterface); + + pub fn create(allocator: std.mem.Allocator) !*@This() { + const self = try allocator.create(@This()); + + self.* = .{ + .allocator = allocator, + }; + + return self; + } + + pub fn registerGame(self: *@This(), gameName: []const u8, p: *anyopaque, comptime InnerType: type) !void { + const interface = try self.allocator.create(GameInterface); + + interface.* = .{ + .ptr = p, + .beginPlayFn = InnerType.beginPlay, + .endPlayFn = InnerType.endPlay, + .gameName = core.MakeName(gameName), + }; + + try self.games.put(self.allocator, gameName, interface); + try self.gamesLinear.append(self.allocator, interface); + } + + pub fn setGameTag(self: *@This(), gameName: []const u8, name: []const u8) void { + var n = core.MakeName(name); + (self.gamesByTag.getOrPut(gameName) catch unreachable).append(n.handle()) catch unreachable; + } + + pub fn removeTag(self: *@This(), game: []const u8, tag: []const u8) void { + var tagName = core.MakeName(tag); + var gameName = core.MakeName(game); + + if (self.gamesByTag.getPtr(tagName.handle())) |list| { + for (list.items) |i| { + if (i.gameName.eql(&gameName)) { + list.swapRemove(i); + return; + } + } + } + } + + pub fn destroy(self: *@This()) void { + var iter = self.games.valueIterator(); + + while (iter.next()) |i| { + self.allocator.destroy(i.*); + } + + self.gamesLinear.deinit(self.allocator); + self.gamesByTag.deinit(self.allocator); + self.games.deinit(self.allocator); + self.allocator.destroy(self); + } +}; + +pub fn beginGame(name: []const u8) void { + getGame(name).beginPlay(); +} + +pub fn endGame(name: []const u8) void { + getGame(name).endPlay(); +} + +pub fn getGame(name: []const u8) *GameInterface { + core.engine_log("getting Game {s}", .{name}); + return core.EngineObject(GameList).get().games.get(name).?; +} + +pub fn addGame(name: []const u8, p: *anyopaque, comptime T: type) !void { + core.engine_log("setting Game {s}", .{name}); + try core.EngineObject(GameList).get().registerGame(name, p, T); +} + +pub const GameModeState = enum { + dead, + prepare, + playing, + paused, +}; + +pub const GameModeMessage = struct { + gameModeName: []const u8, + gameModeRef: ?*anyopaque = null, + gameModeStatus: GameModeState = .dead, +}; + +const std = @import("std"); +const backlog = @import("Backlog"); +const core = backlog.core; diff --git a/lib/p2/src/structures/bump-arena.zig b/lib/p2/src/structures/bump-arena.zig index 6b59464..3036a74 100644 --- a/lib/p2/src/structures/bump-arena.zig +++ b/lib/p2/src/structures/bump-arena.zig @@ -116,7 +116,6 @@ pub const BumpArena = struct { self.large.reset(.free_all); self.small.reset(self.backing); self.smallBumpOffset = 0; - self.latestPageFree = PageSize; self.pageIndex = 0; } }; diff --git a/projects/build.zig b/projects/build.zig index 8060841..d1bb4c8 100644 --- a/projects/build.zig +++ b/projects/build.zig @@ -22,6 +22,7 @@ pub fn build(b: *std.Build) void { sampleGame.setModuleEnabled("physics", true); sampleGame.setModuleEnabled("ui", true); sampleGame.setModuleEnabled("sys", true); + sampleGame.setModuleEnabled("net", true); sampleGame.addExtraModule("gameExtras"); sampleGame.addExtraModule("videoplayer"); sampleGame.addExtraModule("doomplayer"); @@ -32,14 +33,9 @@ pub fn build(b: *std.Build) void { _ = externGame.compileInstall(); } + sampleGame.setIconPath("icons/icon.ico"); const sampleGameExe = sampleGame.compileInstall(); - - if (target.result.os.tag == .windows) { - sampleGameExe.addWin32ResourceFile(.{ - .file = b.path("sampleGame/sampleGame.rc"), - .flags = &.{}, - }); - } + _ = sampleGameExe; // tools diff --git a/projects/sampleGame/externGame/externGame.zig b/projects/sampleGame/externGame/externGame.zig index 491b87f..4d1652f 100644 --- a/projects/sampleGame/externGame/externGame.zig +++ b/projects/sampleGame/externGame/externGame.zig @@ -42,6 +42,54 @@ pub fn start_module(args: core.ModuleLoaderArgs) !void { core.PatchStruct(ExternGameObject, @ptrCast(@alignCast(ref.ptr)), ref.vtable.fieldList.?); } +pub const BoxObject = struct { + playing: bool = false, + + pub fn create(allocator: std.mem.Allocator, entity: core.Entity, params: core.SpawnParameters) !*@This() { + const position = params.posRot.position; + + const box2 = entity; + const scene = box2.addComponent(core.Scene).?; + + const mesh = box2.addComponent(rend.MeshComponent).?; + mesh.setMesh("m_crate"); + mesh.setTexture("t_crate"); + + scene.setPosition(position); + scene.setScaleV(params.posRot.scale); + scene.setMobility(.moveable); + + const collider = box2.addComponent(physics.PhysicsCollider).?; + try collider.setupByShapeName(core.MakeName("ph_small_box"), .{ + .motion_type = .dynamic, + .object_layer = physics.ObjectLayers.moving, + .friction = 0.8, + .mass_properties_override = .{ .mass = 12 }, + .override_mass_properties = .calc_inertia, + }); + + return try allocator.create(@This()); + } + + pub fn createSmoky(allocator: std.mem.Allocator, entity: core.Entity, params: core.SpawnParameters) !*@This() { + const rv = try create(allocator, entity, params); + + const particle = entity.addComponent(rend.ParticleEmitter).?; + particle.start(); + particle.gravity = .{ .y = 1.0 }; + particle.particleVelocity = .{ .min = 0.6, .max = 1.2 }; + particle.particleLife = .{ .min = 5, .max = 6 }; + var name = core.MakeName("t_pixelSmoke"); + particle.texture = rend.getTexture(&name).?; + + return rv; + } + + pub fn destroy(self: *@This(), alloc: std.mem.Allocator) void { + alloc.destroy(self); + } +}; + pub const ExternGameObject = struct { pub var NeonObjectTable = core.EngineObjectVTable.from(@This(), "game.ExternGameObject"); pub const Slack = core.SlackStruct(@This(), 512); @@ -58,6 +106,8 @@ pub const ExternGameObject = struct { //lmao2: bool = true, // lib: bool = false, //s: u32 = 0x42, + // + firstTick: bool = true, fireInput: ?*core.ActionBinding = null, altFireInput: ?*core.ActionBinding = null, @@ -68,6 +118,8 @@ pub const ExternGameObject = struct { Anormal: core.Vector2f = .{ .x = -1, .y = 3 }, Bnormal: core.Vector2f = .{ .x = -1, .y = -3 }, + spawnedEntities: std.ArrayListUnmanaged(core.Entity) = .{}, + displayVectorsTest: bool = false, pub fn beginPlay(p: *anyopaque) void { @@ -126,6 +178,9 @@ pub const ExternGameObject = struct { self.physicsObjectsWindow = try extras.PhysicsObjectList.create(self.allocator); self.consoleWindow.setup(); + try core.registerObject(BoxObject, "Box"); + try core.registerObjectAdvanced(BoxObject, "BoxSmoky", "createSmoky"); + const settings: physics.ShapeSettings = .{ .box = try physics.BoxShapeSettings.create(.{ 1.0, 1.0, 1.0 }) }; defer settings.release(); try physics.addShape("ph_small_box", settings); @@ -238,36 +293,6 @@ pub const ExternGameObject = struct { core.debugSphere(v2ToV3(vp), 0.1, .{ .color = .{ .y = 1.0, .z = 1.0, .x = 1.0 } }); } - pub fn addBox(position: core.Vectorf) !void { - const box2 = try core.createEntity(); - const scene = box2.addComponent(core.Scene).?; - - const mesh = box2.addComponent(rend.MeshComponent).?; - mesh.setMesh("m_crate"); - mesh.setTexture("t_crate"); - - scene.setPosition(position); - scene.setScaleV(core.Vectorf.fromInt(1.0)); - scene.setMobility(.moveable); - - const collider = box2.addComponent(physics.PhysicsCollider).?; - try collider.setupByShapeName(core.MakeName("ph_small_box"), .{ - .motion_type = .dynamic, - .object_layer = physics.ObjectLayers.moving, - .friction = 0.8, - .mass_properties_override = .{ .mass = 12 }, - .override_mass_properties = .calc_inertia, - }); - - const particle = box2.addComponent(rend.ParticleEmitter).?; - particle.start(); - particle.gravity = .{ .y = 1.0 }; - particle.particleVelocity = .{ .min = 0.6, .max = 1.2 }; - particle.particleLife = .{ .min = 5, .max = 6 }; - var name = core.MakeName("t_pixelSmoke"); - particle.texture = rend.getTexture(&name).?; - } - fn onAltFire(ctx: ?*anyopaque, _: core.ActionEvent) void { const self: *@This() = @ptrCast(@alignCast(ctx)); @@ -295,9 +320,14 @@ pub const ExternGameObject = struct { const ray = rend.context().activeCamera.?.getNormalRayFromScreen(platform.getCursorPosition()); if (physics.traceLine(ray.start, ray.dir.fmul(10000), .{ .bodyFilter = &self.fireTraceFilter, .getNormalsSlow = true })) |r| { if (r.normal) |n| { - const p = r.point.add(n.fmul(0.2)); + const p: core.Vectorf = r.point.add(n.fmul(0.2)); - addBox(p.add(n.fmul(1.0))) catch unreachable; + const posRot = core.ScenePosRot{ + .position = p, + }; + self.spawnedEntities.append(self.allocator, core.get(extras.ObjectSystemSpawner).spawnObjectAt(posRot) orelse return) catch unreachable; + + // addBox(p.add(n.fmul(1.0))) catch unreachable; } } } @@ -316,6 +346,11 @@ pub const ExternGameObject = struct { self.drawVector2Test(); } + if (self.firstTick) { + self.loadMap2() catch unreachable; + self.firstTick = false; + } + if (platform.context().isCursorEnabled()) { if (ig.begin("meh", null, .{})) { // if (ig.checkbox("move lights ", null)) {} @@ -372,6 +407,11 @@ pub const ExternGameObject = struct { if (self.tbMap) |map| { map.destroy(); } + for (self.spawnedEntities.items) |*entity| { + entity.destroy(); + } + + self.spawnedEntities.deinit(self.allocator); self.consoleWindow.deinit(); self.physicsObjectsWindow.destroy(); self.allocator.destroy(Slack.fromPtr(self)); diff --git a/projects/sampleGame/main.zig b/projects/sampleGame/main.zig index 7e34634..1d764ba 100644 --- a/projects/sampleGame/main.zig +++ b/projects/sampleGame/main.zig @@ -154,15 +154,23 @@ pub const ParticleObject = struct { }; pub const FoxObject = struct { - data: core.GameObjectData = undefined, entity: core.Entity, - pub fn create(alloc: std.mem.Allocator) !*@This() { + pub const MessageHandlers: []const []const u8 = &.{ + "onGameModeMessage", + }; + + pub fn create(alloc: std.mem.Allocator, entity: core.Entity, p: core.SpawnParameters) !*@This() { const self = try alloc.create(@This()); - const fox = try core.createEntity(); + //const fox = try core.createEntity(); + const fox = entity; const scene = fox.addComponent(core.Scene).?; - scene.setScale(0.01, 0.01, 0.01); + var pr = p.posRot; + pr.scale = pr.scale.fmul(0.01); + scene.setPosRot(pr); + scene.updateTransform(); + const mesh = fox.addComponent(rend.MeshComponent).?; mesh.setMesh("m_fox"); mesh.setTexture("t_fox"); @@ -176,13 +184,19 @@ pub const FoxObject = struct { return self; } + pub fn onGameModeMessage(self: *@This(), messageInfo: *const core.MessageInfo, gameModeMessage: *extras.GameModeMessage) void { + _ = self; + _ = messageInfo; + _ = gameModeMessage; + } + pub fn getEntity(self: *@This()) core.Entity { return self.entity; } - pub fn destroy(self: *@This()) void { - self.entity.destroy(); - self.data.release(self); + pub fn destroy(self: *@This(), allocator: std.mem.Allocator) void { + allocator.destroy(self); + core.engine_logs("destroying fox"); } }; @@ -249,19 +263,23 @@ pub fn prepare(self: *@This()) !void { // core.fs().watchPath("zig-out/modules"); // try core.fs().addFileChangedCallback(core.sharedLibName("externGame"), moduleChangedCallback, self); - // self.rendererDebugger = try extras.RendererDebug.create(self.allocator); //try core.createObject(extras.RendererDebug, .{}); const rendererDebug = try core.createObject(extras.RendererDebug, .{}); try rendererDebug.skyboxes.append(rendererDebug.allocator, core.MakeName("t_skybox")); try rendererDebug.skyboxes.append(rendererDebug.allocator, core.MakeName("t_dark_skybox")); + const objectSpawner2 = try core.createObject(extras.ObjectSystemSpawner, .{}); + _ = objectSpawner2; + //self.objectSpawner = try extras.ObjectSpawner.create(self.allocator); self.objectSpawner = try core.createObject(extras.ObjectSpawner, .{}); - try self.objectSpawner.addSpawnFunction("fox", FoxObject); + // try self.objectSpawner.addSpawnFunction("fox", FoxObject); try self.objectSpawner.addSpawnFunction("particle", ParticleObject); try self.objectSpawner.addSpawnFunction("doomplayer", DoomPlayer.DoomCanvas); try self.objectSpawner.addSpawnFunction("empire", @import("empire.zig")); try self.objectSpawner.addSpawnFunction("DamagedHelmet", DamagedHelmet); + try core.getEngineObject(core.GameObjectSystem).?.registerObject("foxObject", FoxObject); + try assets.loadList(assetReferences); // self.videoplayer = try VideoPlayer.create(self.allocator); // try self.videoplayer.startPlayback("LAPWING2.ogv");