refactor(scene): factor out player location into a persistent component

This commit is contained in:
xeon
2026-02-04 15:32:04 +03:00
parent 3acc274a7d
commit 66dff8ddb1
6 changed files with 109 additions and 43 deletions

View File

@@ -20,6 +20,9 @@ const char_bag_teams_file = "teams";
const char_bag_meta_file = "meta"; const char_bag_meta_file = "meta";
const item_bag_path = "item_bag"; const item_bag_path = "item_bag";
const item_bag_weapon_depot_file = "weapon_depot"; const item_bag_weapon_depot_file = "weapon_depot";
const scene_path = "scene";
const scene_current_file = "current";
const default_level = "map02_lv001";
const default_team: []const []const u8 = &.{ const default_team: []const []const u8 = &.{
"chr_0026_lastrite", "chr_0026_lastrite",
@@ -85,6 +88,11 @@ pub fn loadPlayer(io: Io, gpa: Allocator, assets: *const Assets, uid: []const u8
else => |e| return e, else => |e| return e,
}; };
result.scene = loadSceneComponent(io, data_dir, uid) catch |err| switch (err) {
error.NeedsReset => try createDefaultSceneComponent(io, data_dir, assets),
else => |e| return e,
};
return result; return result;
} }
@@ -446,6 +454,75 @@ fn createDefaultBitsetComponent(io: Io, data_dir: Io.Dir, assets: *const Assets)
return bitset; return bitset;
} }
fn loadSceneComponent(io: Io, data_dir: Io.Dir, uid: []const u8) !Player.Scene {
const scene_dir = data_dir.openDir(io, scene_path, .{}) catch |err| switch (err) {
error.FileNotFound => return error.NeedsReset,
error.Canceled => |e| return e,
else => return error.InputOutput,
};
defer scene_dir.close(io);
const current = fs.loadStruct(Player.Scene.Current, io, scene_dir, scene_current_file) catch |err| switch (err) {
inline error.FileNotFound, error.ChecksumMismatch, error.ReprSizeMismatch => |e| {
if (e == error.ChecksumMismatch) {
log.err(
"checksum mismatched for current scene data of player {s}, resetting to defaults.",
.{uid},
);
}
if (e == error.ReprSizeMismatch) {
log.err(
"struct layout mismatched for current scene data of player {s}, resetting to defaults.",
.{uid},
);
}
return error.NeedsReset;
},
error.Canceled => |e| return e,
else => return error.InputOutput,
};
return .{ .current = current };
}
fn createDefaultSceneComponent(io: Io, data_dir: Io.Dir, assets: *const Assets) !Player.Scene {
const scene_dir = data_dir.createDirPathOpen(io, scene_path, .{}) catch |err| switch (err) {
error.Canceled => |e| return e,
else => return error.InputOutput,
};
const default_level_config = assets.level_config_table.getPtr(default_level).?;
const current: Player.Scene.Current = .{
.level_id = default_level_config.idNum,
.position = .{
default_level_config.playerInitPos.x,
default_level_config.playerInitPos.y,
default_level_config.playerInitPos.z,
},
.rotation = .{
default_level_config.playerInitRot.x,
default_level_config.playerInitRot.y,
default_level_config.playerInitRot.z,
},
};
try fs.saveStruct(Player.Scene.Current, &current, io, scene_dir, scene_current_file);
return .{ .current = current };
}
pub fn saveSceneComponent(io: Io, data_dir: Io.Dir, component: *const Player.Scene) !void {
const scene_dir = data_dir.createDirPathOpen(io, scene_path, .{}) catch |err| switch (err) {
error.Canceled => |e| return e,
else => return error.InputOutput,
};
try fs.saveStruct(Player.Scene.Current, &component.current, io, scene_dir, scene_current_file);
}
fn loadArray( fn loadArray(
comptime T: type, comptime T: type,
io: Io, io: Io,

View File

@@ -10,6 +10,7 @@ pub const Unlock = @import("Player/Unlock.zig");
pub const CharBag = @import("Player/CharBag.zig"); pub const CharBag = @import("Player/CharBag.zig");
pub const ItemBag = @import("Player/ItemBag.zig"); pub const ItemBag = @import("Player/ItemBag.zig");
pub const Bitset = @import("Player/Bitset.zig"); pub const Bitset = @import("Player/Bitset.zig");
pub const Scene = @import("Player/Scene.zig");
base: Base, base: Base,
game_vars: GameVars, game_vars: GameVars,
@@ -17,6 +18,7 @@ unlock: Unlock,
char_bag: CharBag, char_bag: CharBag,
item_bag: ItemBag, item_bag: ItemBag,
bitset: Bitset, bitset: Bitset,
scene: Scene,
pub fn deinit(player: *Player, gpa: Allocator) void { pub fn deinit(player: *Player, gpa: Allocator) void {
player.game_vars.deinit(gpa); player.game_vars.deinit(gpa);

View File

@@ -0,0 +1,9 @@
const Scene = @This();
current: Current,
pub const Current = struct {
level_id: i32,
position: [3]f32,
rotation: [3]f32,
};

View File

@@ -15,32 +15,10 @@ pub const PlayerId = struct {
uid: mem.LimitedString(max_length), uid: mem.LimitedString(max_length),
}; };
// TODO: this can be made a persistent Player component.
pub const Location = struct {
const default_level = "map02_lv001";
level: i32,
position: [3]f32,
fn createDefault(assets: *const Assets) Location {
const level_config = assets.level_config_table.getPtr(default_level).?;
return .{
.level = level_config.idNum,
.position = .{
level_config.playerInitPos.x,
level_config.playerInitPos.y,
level_config.playerInitPos.z,
},
};
}
};
player_id: PlayerId, player_id: PlayerId,
session: *Session, // TODO: should it be here this way? Do we need an abstraction? session: *Session, // TODO: should it be here this way? Do we need an abstraction?
res: logic.Resource, res: logic.Resource,
player: logic.Player, player: logic.Player,
location: Location,
pub fn init( pub fn init(
session: *Session, session: *Session,
@@ -56,7 +34,6 @@ pub fn init(
.session = session, .session = session,
.player = player, .player = player,
.res = .init(assets, io), .res = .init(assets, io),
.location = .createDefault(assets),
}; };
} }
@@ -71,7 +48,6 @@ pub const GetComponentError = error{
pub fn getComponentByType(world: *World, comptime T: type) GetComponentError!T { pub fn getComponentByType(world: *World, comptime T: type) GetComponentError!T {
switch (T) { switch (T) {
PlayerId => return world.player_id, PlayerId => return world.player_id,
*Location, *const Location => return &world.location,
*Session => return world.session, *Session => return world.session,
*logic.Resource.PingTimer => return &world.res.ping_timer, *logic.Resource.PingTimer => return &world.res.ping_timer,
*const Assets => return world.res.assets, *const Assets => return world.res.assets,

View File

@@ -2,12 +2,14 @@ const std = @import("std");
const pb = @import("proto").pb; const pb = @import("proto").pb;
const logic = @import("../../logic.zig"); const logic = @import("../../logic.zig");
const Assets = @import("../../Assets.zig"); const Assets = @import("../../Assets.zig");
const Player = logic.Player;
const messaging = logic.messaging; const messaging = logic.messaging;
pub fn onSceneSetTrackPoint( pub fn onSceneSetTrackPoint(
request: messaging.Request(pb.CS_SCENE_SET_TRACK_POINT), request: messaging.Request(pb.CS_SCENE_SET_TRACK_POINT),
assets: *const Assets, assets: *const Assets,
location: *logic.World.Location, scene: Player.Component(.scene),
change_scene_tx: logic.event.Sender(.change_scene_begin), change_scene_tx: logic.event.Sender(.change_scene_begin),
) !void { ) !void {
const log = std.log.scoped(.scene_set_track_point); const log = std.log.scoped(.scene_set_track_point);
@@ -36,8 +38,8 @@ pub fn onSceneSetTrackPoint(
return; return;
}; };
location.level = level_config.idNum; scene.data.current.level_id = level_config.idNum;
location.position = .{ scene.data.current.position = .{
validation_config.position.x, validation_config.position.x,
validation_config.position.y, validation_config.position.y,
validation_config.position.z, validation_config.position.z,
@@ -47,6 +49,6 @@ pub fn onSceneSetTrackPoint(
log.info( log.info(
"transitioning to scene '{s}', position: {any}", "transitioning to scene '{s}', position: {any}",
.{ validation_config.sceneId, location.position }, .{ validation_config.sceneId, scene.data.current.position },
); );
} }

View File

@@ -19,26 +19,26 @@ pub fn enterSceneOnLogin(
pub fn beginChangingScene( pub fn beginChangingScene(
rx: logic.event.Receiver(.change_scene_begin), rx: logic.event.Receiver(.change_scene_begin),
session: *Session, session: *Session,
base_comp: Player.Component(.base), base: Player.Component(.base),
location: *const logic.World.Location, scene: Player.Component(.scene),
) !void { ) !void {
_ = rx; _ = rx;
const position: pb.VECTOR = .{ const position: pb.VECTOR = .{
.X = location.position[0], .X = scene.data.current.position[0],
.Y = location.position[1], .Y = scene.data.current.position[1],
.Z = location.position[2], .Z = scene.data.current.position[2],
}; };
try session.send(pb.SC_CHANGE_SCENE_BEGIN_NOTIFY{ try session.send(pb.SC_CHANGE_SCENE_BEGIN_NOTIFY{
.scene_num_id = location.level, .scene_num_id = scene.data.current.level_id,
.position = position, .position = position,
.pass_through_data = .init, .pass_through_data = .init,
}); });
try session.send(pb.SC_ENTER_SCENE_NOTIFY{ try session.send(pb.SC_ENTER_SCENE_NOTIFY{
.role_id = base_comp.data.role_id, .role_id = base.data.role_id,
.scene_num_id = location.level, .scene_num_id = scene.data.current.level_id,
.position = position, .position = position,
.pass_through_data = .init, .pass_through_data = .init,
}); });
@@ -62,8 +62,8 @@ pub fn syncSelfScene(
rx: logic.event.Receiver(.sync_self_scene), rx: logic.event.Receiver(.sync_self_scene),
session: *Session, session: *Session,
arena: logic.Resource.Allocator(.arena), arena: logic.Resource.Allocator(.arena),
char_bag: logic.Player.Component(.char_bag), char_bag: Player.Component(.char_bag),
location: *const logic.World.Location, scene: Player.Component(.scene),
assets: *const Assets, assets: *const Assets,
) !void { ) !void {
const reason: pb.SELF_INFO_REASON_TYPE = switch (rx.payload.reason) { const reason: pb.SELF_INFO_REASON_TYPE = switch (rx.payload.reason) {
@@ -72,16 +72,16 @@ pub fn syncSelfScene(
}; };
const position: pb.VECTOR = .{ const position: pb.VECTOR = .{
.X = location.position[0], .X = scene.data.current.position[0],
.Y = location.position[1], .Y = scene.data.current.position[1],
.Z = location.position[2], .Z = scene.data.current.position[2],
}; };
const team_index = char_bag.data.meta.curr_team_index; const team_index = char_bag.data.meta.curr_team_index;
const leader_index = char_bag.data.teams.items(.leader_index)[team_index]; const leader_index = char_bag.data.teams.items(.leader_index)[team_index];
var self_scene_info: pb.SC_SELF_SCENE_INFO = .{ var self_scene_info: pb.SC_SELF_SCENE_INFO = .{
.scene_num_id = location.level, .scene_num_id = scene.data.current.level_id,
.self_info_reason = @intFromEnum(reason), .self_info_reason = @intFromEnum(reason),
.teamInfo = .{ .teamInfo = .{
.team_type = .CHAR_BAG_TEAM_TYPE_MAIN, .team_type = .CHAR_BAG_TEAM_TYPE_MAIN,
@@ -111,7 +111,7 @@ pub fn syncSelfScene(
.templateid = char_template_id, .templateid = char_template_id,
.position = position, .position = position,
.rotation = .{}, .rotation = .{},
.scene_num_id = location.level, .scene_num_id = scene.data.current.level_id,
}, },
}; };