mirror of
https://git.xeondev.com/LR/S.git
synced 2026-03-22 07:32:21 +01:00
Release 0.1.0
This commit is contained in:
45
gamesv/src/logic/Player.zig
Normal file
45
gamesv/src/logic/Player.zig
Normal file
@@ -0,0 +1,45 @@
|
||||
const Player = @This();
|
||||
const std = @import("std");
|
||||
const meta = std.meta;
|
||||
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
pub const Base = @import("Player/Base.zig");
|
||||
pub const GameVars = @import("Player/GameVars.zig");
|
||||
pub const Unlock = @import("Player/Unlock.zig");
|
||||
pub const CharBag = @import("Player/CharBag.zig");
|
||||
pub const ItemBag = @import("Player/ItemBag.zig");
|
||||
pub const Bitset = @import("Player/Bitset.zig");
|
||||
|
||||
base: Base,
|
||||
game_vars: GameVars,
|
||||
unlock: Unlock,
|
||||
char_bag: CharBag,
|
||||
item_bag: ItemBag,
|
||||
bitset: Bitset,
|
||||
|
||||
pub fn deinit(player: *Player, gpa: Allocator) void {
|
||||
player.game_vars.deinit(gpa);
|
||||
player.unlock.deinit(gpa);
|
||||
player.char_bag.deinit(gpa);
|
||||
player.item_bag.deinit(gpa);
|
||||
}
|
||||
|
||||
// Describes the dependency on an individual player component.
|
||||
pub fn Component(comptime tag: meta.FieldEnum(Player)) type {
|
||||
return struct {
|
||||
pub const player_component_tag = tag;
|
||||
|
||||
data: *@FieldType(Player, @tagName(tag)),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn isComponent(comptime T: type) bool {
|
||||
if (!@hasDecl(T, "player_component_tag")) return false;
|
||||
|
||||
return T == Component(T.player_component_tag);
|
||||
}
|
||||
|
||||
pub fn getComponentByType(player: *Player, comptime T: type) T {
|
||||
return .{ .data = &@field(player, @tagName(T.player_component_tag)) };
|
||||
}
|
||||
37
gamesv/src/logic/Player/Base.zig
Normal file
37
gamesv/src/logic/Player/Base.zig
Normal file
@@ -0,0 +1,37 @@
|
||||
const Base = @This();
|
||||
const common = @import("common");
|
||||
const mem = common.mem;
|
||||
|
||||
pub const max_role_name_length: usize = 15;
|
||||
|
||||
pub const init: Base = .{
|
||||
.create_ts = 0,
|
||||
.role_name = .constant("xeondev"),
|
||||
.role_id = 1,
|
||||
.level = .first,
|
||||
.exp = 0,
|
||||
.create_ts_display = 0,
|
||||
.gender = .default,
|
||||
};
|
||||
|
||||
pub const Gender = enum(u8) {
|
||||
pub const default: Gender = .male;
|
||||
|
||||
invalid = 0,
|
||||
male = 1,
|
||||
female = 2,
|
||||
};
|
||||
|
||||
pub const Level = enum(u8) {
|
||||
first = 1,
|
||||
last = 60,
|
||||
_,
|
||||
};
|
||||
|
||||
create_ts: i64,
|
||||
role_name: mem.LimitedString(max_role_name_length),
|
||||
role_id: u64,
|
||||
level: Level,
|
||||
exp: u32,
|
||||
create_ts_display: i64,
|
||||
gender: Gender,
|
||||
113
gamesv/src/logic/Player/Bitset.zig
Normal file
113
gamesv/src/logic/Player/Bitset.zig
Normal file
@@ -0,0 +1,113 @@
|
||||
const Bitset = @This();
|
||||
const std = @import("std");
|
||||
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
pub const max_value = 512;
|
||||
const Set = std.bit_set.ArrayBitSet(u64, max_value);
|
||||
|
||||
pub const init: Bitset = .{};
|
||||
|
||||
sets: [Type.count]Set = @splat(.initEmpty()),
|
||||
|
||||
pub fn set(b: *Bitset, t: Type, value: u64) error{ValueOutOfRange}!void {
|
||||
if (value > max_value) return error.ValueOutOfRange;
|
||||
|
||||
b.sets[@intFromEnum(t) - 1].set(@intCast(value));
|
||||
}
|
||||
|
||||
pub const Type = enum(u32) {
|
||||
pub const count: usize = blk: {
|
||||
const values = std.enums.values(Type);
|
||||
break :blk @as(usize, @intFromEnum(values[values.len - 1])) + 1;
|
||||
};
|
||||
|
||||
found_item = 1,
|
||||
wiki = 2,
|
||||
unread_wiki = 3,
|
||||
monster_drop = 4,
|
||||
got_item = 5,
|
||||
area_first_view = 6,
|
||||
unread_got_item = 7,
|
||||
prts = 8,
|
||||
unread_prts = 9,
|
||||
prts_first_lv = 10,
|
||||
prts_terminal_content = 11,
|
||||
level_have_been = 12,
|
||||
level_map_first_view = 13,
|
||||
unread_formula = 14,
|
||||
new_char = 15,
|
||||
elog_channel = 16,
|
||||
fmv_watched = 17,
|
||||
time_line_watched = 18,
|
||||
map_filter = 19,
|
||||
friend_has_request = 20,
|
||||
equip_tech_formula = 21,
|
||||
radio_trigger = 22,
|
||||
remote_communication_finish = 23,
|
||||
unlock_server_dungeon_series = 24,
|
||||
chapter_first_view = 25,
|
||||
adventure_level_reward_done = 26,
|
||||
dungeon_entrance_touched = 27,
|
||||
equip_tech_tier = 28,
|
||||
char_doc = 30,
|
||||
char_voice = 31,
|
||||
reading_pop = 32,
|
||||
reward_id_done = 33,
|
||||
prts_investigate = 34,
|
||||
racing_received_bp_node = 35,
|
||||
racing_complete_achievement = 36,
|
||||
racing_received_achievement = 37,
|
||||
interactive_active = 39,
|
||||
mine_point_first_time_collect = 40,
|
||||
unread_char_doc = 41,
|
||||
unread_char_voice = 42,
|
||||
area_toast_once = 44,
|
||||
unread_equip_tech_formula = 45,
|
||||
prts_investigate_unread_note = 46,
|
||||
prts_investigate_note = 47,
|
||||
game_mechanic_read = 48,
|
||||
read_active_blackbox = 49,
|
||||
read_level = 50,
|
||||
factroy_placed_building = 51,
|
||||
interactive_two_state = 52,
|
||||
unread_unlock_spaceship_room_type = 53,
|
||||
unlock_spaceship_room_type = 54,
|
||||
unlock_user_avatar = 55,
|
||||
unlock_user_avatar_frame = 56,
|
||||
unlock_business_card_topic = 57,
|
||||
special_game_event = 58,
|
||||
radio_id = 59,
|
||||
got_weapon = 60,
|
||||
read_new_version_equip_tech_formula = 61,
|
||||
mist_map_unlocked = 62,
|
||||
read_achive = 63,
|
||||
camera_volume = 64,
|
||||
read_fac_tech_tree_unhidden_tech = 65,
|
||||
read_fac_tech_tree_unhidden_category = 66,
|
||||
mist_map_mv_watched = 67,
|
||||
remote_communication_wait_for_play = 68,
|
||||
mission_completed_once = 69,
|
||||
psn_cup_unlocked = 70,
|
||||
unread_week_raid_mission = 71,
|
||||
unlock_game_entrance_activity_series = 72,
|
||||
unlock_domain_depot = 73,
|
||||
unlock_recycle_bin = 74,
|
||||
manual_crafted_item = 75,
|
||||
un_read_new_activity_notify = 76,
|
||||
read_picture_ids = 77,
|
||||
read_shop_id = 78,
|
||||
read_shop_goods_id = 79,
|
||||
read_bp_season_id = 80,
|
||||
read_bp_task_id = 81,
|
||||
read_cash_shop_goods_id = 82,
|
||||
new_avatar_unlock = 83,
|
||||
new_avatar_frame_unlock = 84,
|
||||
new_theme_unlock = 85,
|
||||
read_char_potential_pic_ids = 86,
|
||||
read_high_difficulty_dungeon_series = 87,
|
||||
reported_client_log_types = 88,
|
||||
activated_factory_inst = 89,
|
||||
read_max_world_level = 90,
|
||||
got_formula_unlock_item = 91,
|
||||
};
|
||||
102
gamesv/src/logic/Player/CharBag.zig
Normal file
102
gamesv/src/logic/Player/CharBag.zig
Normal file
@@ -0,0 +1,102 @@
|
||||
const CharBag = @This();
|
||||
const std = @import("std");
|
||||
const common = @import("common");
|
||||
const Player = @import("../Player.zig");
|
||||
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
teams: std.MultiArrayList(Team),
|
||||
chars: std.MultiArrayList(Char),
|
||||
meta: Meta,
|
||||
|
||||
pub const CharIndex = enum(u64) {
|
||||
_,
|
||||
|
||||
// Returns an 'objectId' for network serialization.
|
||||
pub fn objectId(i: CharIndex) u64 {
|
||||
return @intFromEnum(i) + 1;
|
||||
}
|
||||
|
||||
pub fn fromObjectId(id: u64) CharIndex {
|
||||
return @enumFromInt(id - 1);
|
||||
}
|
||||
};
|
||||
|
||||
pub fn deinit(bag: *CharBag, gpa: Allocator) void {
|
||||
bag.teams.deinit(gpa);
|
||||
bag.chars.deinit(gpa);
|
||||
}
|
||||
|
||||
pub fn charIndexById(bag: *const CharBag, template_id: i32) ?CharIndex {
|
||||
const idx: u64 = @intCast(
|
||||
std.mem.findScalar(i32, bag.chars.items(.template_id), template_id) orelse
|
||||
return null,
|
||||
);
|
||||
|
||||
return @enumFromInt(idx);
|
||||
}
|
||||
|
||||
pub fn charIndexWithWeapon(bag: *const CharBag, weapon: Player.ItemBag.WeaponIndex) ?CharIndex {
|
||||
const idx: u64 = @intCast(
|
||||
std.mem.findScalar(Player.ItemBag.WeaponIndex, bag.chars.items(.weapon_id), weapon) orelse
|
||||
return null,
|
||||
);
|
||||
|
||||
return @enumFromInt(idx);
|
||||
}
|
||||
|
||||
// Checks:
|
||||
// 1. Existence of the team.
|
||||
// 2. Existence of the specified character index in the team.
|
||||
pub fn ensureTeamMember(bag: *const CharBag, team_index: usize, char_index: CharIndex) error{
|
||||
InvalidTeamIndex,
|
||||
NotTeamMember,
|
||||
}!void {
|
||||
if (team_index < 0 or team_index >= bag.teams.len) {
|
||||
return error.InvalidTeamIndex;
|
||||
}
|
||||
|
||||
const char_team = &bag.teams.items(.char_team)[team_index];
|
||||
|
||||
_ = std.mem.findScalar(u64, @ptrCast(char_team), @intFromEnum(char_index)) orelse
|
||||
return error.NotTeamMember;
|
||||
}
|
||||
|
||||
pub const Meta = struct {
|
||||
curr_team_index: u32,
|
||||
};
|
||||
|
||||
pub const Team = struct {
|
||||
pub const slots_count: usize = 4;
|
||||
pub const SlotArray = [Team.slots_count]Team.Slot;
|
||||
|
||||
pub const Slot = enum(u64) {
|
||||
empty = std.math.maxInt(u64),
|
||||
_,
|
||||
|
||||
pub fn charIndex(s: Slot) ?CharIndex {
|
||||
return if (s != .empty) @enumFromInt(@intFromEnum(s)) else null;
|
||||
}
|
||||
|
||||
pub fn fromCharIndex(i: CharIndex) Slot {
|
||||
return @enumFromInt(@intFromEnum(i));
|
||||
}
|
||||
};
|
||||
|
||||
name: common.mem.LimitedString(15) = .empty,
|
||||
char_team: [slots_count]Slot = @splat(Slot.empty),
|
||||
leader_index: CharIndex,
|
||||
};
|
||||
|
||||
pub const Char = struct {
|
||||
template_id: i32,
|
||||
level: i32,
|
||||
exp: i32,
|
||||
is_dead: bool,
|
||||
hp: f64,
|
||||
ultimate_sp: f32,
|
||||
weapon_id: Player.ItemBag.WeaponIndex,
|
||||
own_time: i64,
|
||||
equip_medicine_id: i32,
|
||||
potential_level: u32,
|
||||
};
|
||||
71
gamesv/src/logic/Player/GameVars.zig
Normal file
71
gamesv/src/logic/Player/GameVars.zig
Normal file
@@ -0,0 +1,71 @@
|
||||
const GameVars = @This();
|
||||
const std = @import("std");
|
||||
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
pub const default_server_vars: []const ServerVar = &.{
|
||||
.{ .key = .already_set_gender, .value = 1 },
|
||||
.{ .key = .dash_energy_limit, .value = 100 },
|
||||
.{ .key = .already_set_name, .value = 1 },
|
||||
};
|
||||
|
||||
pub const default_client_vars: []const ClientVar = &.{
|
||||
.{ .key = 43, .value = 1 },
|
||||
.{ .key = 78, .value = 1 },
|
||||
.{ .key = 82, .value = 1 },
|
||||
.{ .key = 125, .value = 1 },
|
||||
.{ .key = 126, .value = 1 },
|
||||
};
|
||||
|
||||
pub const ClientVar = packed struct {
|
||||
key: i32,
|
||||
value: i64,
|
||||
};
|
||||
|
||||
pub const ServerVar = packed struct {
|
||||
key: ServerVarType,
|
||||
value: i64,
|
||||
};
|
||||
|
||||
client_vars: []ClientVar,
|
||||
server_vars: []ServerVar,
|
||||
|
||||
pub fn deinit(gv: *GameVars, gpa: Allocator) void {
|
||||
gpa.free(gv.client_vars);
|
||||
gpa.free(gv.server_vars);
|
||||
}
|
||||
|
||||
pub const ServerVarType = enum(i32) {
|
||||
const common_begin: i32 = 100000;
|
||||
const common_end: i32 = 109999;
|
||||
const daily_refresh_begin: i32 = 110000;
|
||||
const daily_refresh_end: i32 = 119999;
|
||||
|
||||
pub const Kind = enum(i32) {
|
||||
common = 10,
|
||||
daily_refresh = 11,
|
||||
weekly_refresh = 12,
|
||||
monthly_refresh = 13,
|
||||
};
|
||||
|
||||
server_test_1 = 100001,
|
||||
server_test_2 = 100002,
|
||||
already_set_gender = 100003,
|
||||
enhance_bean = 100004,
|
||||
enhance_bean_last_replenish_time = 100005,
|
||||
dash_energy_limit = 100006,
|
||||
already_set_name = 100007,
|
||||
social_share_control = 100008,
|
||||
db_config_version = 100009,
|
||||
client_debug_mode_end_time = 100010,
|
||||
recover_ap_by_money_count = 110001,
|
||||
poop_cow_interact_count = 110002,
|
||||
stamina_reduce_used_count = 110003,
|
||||
space_ship_daily_credit_reward = 110004,
|
||||
daily_enemy_drop_mod_reward_count = 110005,
|
||||
daily_enemy_exp_count = 110006,
|
||||
|
||||
pub inline fn kind(vt: ServerVarType) Kind {
|
||||
return @enumFromInt(@intFromEnum(vt) / 10_000);
|
||||
}
|
||||
};
|
||||
28
gamesv/src/logic/Player/ItemBag.zig
Normal file
28
gamesv/src/logic/Player/ItemBag.zig
Normal file
@@ -0,0 +1,28 @@
|
||||
const ItemBag = @This();
|
||||
const std = @import("std");
|
||||
const common = @import("common");
|
||||
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
weapon_depot: std.MultiArrayList(Weapon),
|
||||
|
||||
pub fn deinit(bag: *ItemBag, gpa: Allocator) void {
|
||||
bag.weapon_depot.deinit(gpa);
|
||||
}
|
||||
|
||||
pub const WeaponIndex = enum(u64) {
|
||||
_,
|
||||
|
||||
pub fn instId(i: WeaponIndex) u64 {
|
||||
return @intFromEnum(i) + 1;
|
||||
}
|
||||
};
|
||||
|
||||
pub const Weapon = struct {
|
||||
template_id: i32,
|
||||
exp: u32,
|
||||
weapon_lv: u32,
|
||||
refine_lv: u32,
|
||||
breakthrough_lv: u32,
|
||||
attach_gem_id: u64,
|
||||
};
|
||||
126
gamesv/src/logic/Player/Unlock.zig
Normal file
126
gamesv/src/logic/Player/Unlock.zig
Normal file
@@ -0,0 +1,126 @@
|
||||
const Unlock = @This();
|
||||
const std = @import("std");
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
pub const default_unlocked_systems: []const SystemType = blk: {
|
||||
const fields = @typeInfo(SystemType).@"enum".fields;
|
||||
var list: []const SystemType = &.{};
|
||||
|
||||
for (fields) |field| {
|
||||
if (field.value != @intFromEnum(SystemType.none)) {
|
||||
list = list ++ .{@field(SystemType, field.name)};
|
||||
}
|
||||
}
|
||||
|
||||
break :blk list;
|
||||
};
|
||||
|
||||
unlocked_systems: []SystemType,
|
||||
|
||||
pub fn deinit(unlock: *Unlock, gpa: Allocator) void {
|
||||
gpa.free(unlock.unlocked_systems);
|
||||
}
|
||||
|
||||
pub const SystemType = enum(i32) {
|
||||
none = 10000000,
|
||||
map = 0,
|
||||
inventory = 1,
|
||||
watch = 2,
|
||||
valuable_depot = 3,
|
||||
shop = 4,
|
||||
char_team = 5,
|
||||
gacha = 51,
|
||||
dungeon = 52,
|
||||
bloc_mission = 53,
|
||||
mail = 54,
|
||||
wiki = 55,
|
||||
prts = 56,
|
||||
submit_ether = 57,
|
||||
scan = 58,
|
||||
char_ui = 59,
|
||||
friend = 60,
|
||||
daily_mission = 61,
|
||||
general_ability_bomb = 62,
|
||||
general_ability_fluid_interact = 63,
|
||||
general_ability = 64,
|
||||
sns = 65,
|
||||
equip_tech = 66,
|
||||
equip_produce = 67,
|
||||
dungeon_factory = 69,
|
||||
enemy_spawner = 70,
|
||||
general_ability_water_gun = 71,
|
||||
general_ability_snapshot = 72,
|
||||
fac_building_pin = 101,
|
||||
fac_craft_pin = 102,
|
||||
fac_mode = 103,
|
||||
fac_tech_tree = 104,
|
||||
fac_overview = 105,
|
||||
fac_yield_stats = 106,
|
||||
fac_conveyor = 107,
|
||||
fac_transfer_port = 108,
|
||||
fac_bridge = 109,
|
||||
fac_splitter = 110,
|
||||
fac_merger = 111,
|
||||
fac_bus = 112,
|
||||
fac_zone = 113,
|
||||
fac_system = 114,
|
||||
fac_pipe = 115,
|
||||
fac_pipe_splitter = 116,
|
||||
fac_pipe_connector = 117,
|
||||
fac_pipe_converger = 118,
|
||||
fac_hub = 119,
|
||||
fac_bus_free = 120,
|
||||
fac_top_view = 121,
|
||||
fac_blueprint = 122,
|
||||
fac_underground_pipe = 123,
|
||||
fac_social = 124,
|
||||
fac_valve = 125,
|
||||
fac_pipe_valve = 126,
|
||||
fac_panel_store = 127,
|
||||
fac_fertilize = 128,
|
||||
manual_craft = 201,
|
||||
item_use = 202,
|
||||
item_quick_bar = 203,
|
||||
product_manual = 204,
|
||||
manual_craft_soil = 205,
|
||||
weapon = 251,
|
||||
equip = 252,
|
||||
equip_enhance = 253,
|
||||
gem_enhance = 254,
|
||||
normal_attack = 301,
|
||||
normal_skill = 302,
|
||||
ultimate_skill = 303,
|
||||
team_skill = 304,
|
||||
combo_skill = 305,
|
||||
team_switch = 306,
|
||||
dash = 307,
|
||||
jump = 308,
|
||||
lock_target = 309,
|
||||
spaceship_present_gift = 401,
|
||||
spaceship_manufacturing_station = 402,
|
||||
spaceship_control_center = 403,
|
||||
spaceship_system = 404,
|
||||
spaceship_grow_cabin = 405,
|
||||
spaceship_shop = 406,
|
||||
spaceship_guest_room = 407,
|
||||
settlement = 501,
|
||||
domain_development = 502,
|
||||
domain_development_domain_depot = 503,
|
||||
settlement_defense = 504,
|
||||
kite_station = 511,
|
||||
domain_shop = 512,
|
||||
racing_dungeon = 601,
|
||||
battle_training = 602,
|
||||
week_raid = 603,
|
||||
week_raid_intro = 604,
|
||||
water_drone_can_use_xiranite = 605,
|
||||
adventure_exp_and_lv = 651,
|
||||
adventure_book = 652,
|
||||
guidance_manul = 661,
|
||||
ai_bark = 670,
|
||||
achievement = 701,
|
||||
minigame_puzzle = 801,
|
||||
bp_system = 802,
|
||||
activity = 1100,
|
||||
check_in = 1113,
|
||||
};
|
||||
46
gamesv/src/logic/Resource.zig
Normal file
46
gamesv/src/logic/Resource.zig
Normal file
@@ -0,0 +1,46 @@
|
||||
const Resource = @This();
|
||||
const std = @import("std");
|
||||
const mem = std.mem;
|
||||
const Assets = @import("../Assets.zig");
|
||||
|
||||
const Io = std.Io;
|
||||
|
||||
pub const AllocatorKind = enum {
|
||||
gpa,
|
||||
arena,
|
||||
};
|
||||
|
||||
pub const PingTimer = struct {
|
||||
io: Io,
|
||||
last_client_ts: u64 = 0,
|
||||
|
||||
pub fn serverTime(pt: PingTimer) u64 {
|
||||
return if (Io.Clock.real.now(pt.io)) |ts|
|
||||
@intCast(ts.toMilliseconds())
|
||||
else |_|
|
||||
pt.last_client_ts;
|
||||
}
|
||||
};
|
||||
|
||||
assets: *const Assets,
|
||||
ping_timer: PingTimer,
|
||||
|
||||
pub fn init(assets: *const Assets, io_impl: Io) Resource {
|
||||
return .{
|
||||
.assets = assets,
|
||||
.ping_timer = .{ .io = io_impl },
|
||||
};
|
||||
}
|
||||
|
||||
pub fn io(res: *const Resource) Io {
|
||||
return res.ping_timer.io; // TODO: move to the root of resources.
|
||||
}
|
||||
|
||||
// Describes the dependency on an allocator.
|
||||
pub fn Allocator(comptime kind: AllocatorKind) type {
|
||||
return struct {
|
||||
pub const allocator_kind = kind;
|
||||
|
||||
interface: mem.Allocator,
|
||||
};
|
||||
}
|
||||
58
gamesv/src/logic/World.zig
Normal file
58
gamesv/src/logic/World.zig
Normal file
@@ -0,0 +1,58 @@
|
||||
// Describes player-local state of the world.
|
||||
const World = @This();
|
||||
const std = @import("std");
|
||||
const logic = @import("../logic.zig");
|
||||
const Session = @import("../Session.zig");
|
||||
const Assets = @import("../Assets.zig");
|
||||
|
||||
const Io = std.Io;
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
pub const PlayerId = struct { uid: u64 };
|
||||
|
||||
player_id: PlayerId,
|
||||
session: *Session, // TODO: should it be here this way? Do we need an abstraction?
|
||||
res: logic.Resource,
|
||||
player: logic.Player,
|
||||
|
||||
pub fn init(
|
||||
session: *Session,
|
||||
assets: *const Assets,
|
||||
uid: u64,
|
||||
player: logic.Player,
|
||||
gpa: Allocator,
|
||||
io: Io,
|
||||
) World {
|
||||
_ = gpa;
|
||||
return .{
|
||||
.player_id = .{ .uid = uid },
|
||||
.session = session,
|
||||
.player = player,
|
||||
.res = .init(assets, io),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(world: *World, gpa: Allocator) void {
|
||||
world.player.deinit(gpa);
|
||||
}
|
||||
|
||||
pub const GetComponentError = error{
|
||||
ComponentUnavailable,
|
||||
};
|
||||
|
||||
pub fn getComponentByType(world: *World, comptime T: type) GetComponentError!T {
|
||||
switch (T) {
|
||||
PlayerId => return world.player_id,
|
||||
*Session => return world.session,
|
||||
*logic.Resource.PingTimer => return &world.res.ping_timer,
|
||||
*const Assets => return world.res.assets,
|
||||
Io => return world.res.io(),
|
||||
else => {
|
||||
if (comptime logic.Player.isComponent(T)) {
|
||||
return world.player.getComponentByType(T);
|
||||
}
|
||||
|
||||
@compileError("World.getComponentByType(" ++ @typeName(T) ++ ") is unsupported");
|
||||
},
|
||||
}
|
||||
}
|
||||
80
gamesv/src/logic/event.zig
Normal file
80
gamesv/src/logic/event.zig
Normal file
@@ -0,0 +1,80 @@
|
||||
const std = @import("std");
|
||||
pub const kinds = @import("event/kinds.zig");
|
||||
|
||||
const Allocator = std.mem.Allocator;
|
||||
const meta = std.meta;
|
||||
|
||||
// Describes the event receiver
|
||||
pub fn Receiver(comptime kind: meta.Tag(Kind)) type {
|
||||
return struct {
|
||||
pub const rx_event_kind = kind;
|
||||
pub const Event = @field(
|
||||
kinds,
|
||||
@typeInfo(kinds).@"struct".decls[@intFromEnum(kind)].name,
|
||||
);
|
||||
|
||||
payload: Event,
|
||||
};
|
||||
}
|
||||
|
||||
// Describes the event sender
|
||||
pub fn Sender(comptime kind: meta.Tag(Kind)) type {
|
||||
return struct {
|
||||
pub const tx_event_kind = kind;
|
||||
pub const Event = @field(
|
||||
kinds,
|
||||
@typeInfo(kinds).@"struct".decls[@intFromEnum(kind)].name,
|
||||
);
|
||||
|
||||
event_queue: *Queue,
|
||||
|
||||
pub fn send(s: @This(), event: Event) Allocator.Error!void {
|
||||
try s.event_queue.push(@unionInit(Kind, @tagName(kind), event));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub const Queue = struct {
|
||||
arena: Allocator,
|
||||
deque: std.Deque(Kind),
|
||||
|
||||
pub fn init(arena: Allocator) Queue {
|
||||
return .{ .arena = arena, .deque = .empty };
|
||||
}
|
||||
|
||||
pub fn push(queue: *Queue, event: Kind) Allocator.Error!void {
|
||||
try queue.deque.pushBack(queue.arena, event);
|
||||
}
|
||||
};
|
||||
|
||||
pub const Kind = blk: {
|
||||
var types: []const type = &.{};
|
||||
var indices: []const u16 = &.{};
|
||||
var names: []const []const u8 = &.{};
|
||||
|
||||
for (@typeInfo(kinds).@"struct".decls, 0..) |decl, i| {
|
||||
const declaration = @field(kinds, decl.name);
|
||||
if (@TypeOf(declaration) != type) continue;
|
||||
if (meta.activeTag(@typeInfo(declaration)) == .@"struct") {
|
||||
indices = indices ++ .{@as(u16, @intCast(i))};
|
||||
types = types ++ .{@field(kinds, decl.name)};
|
||||
names = names ++ .{toSnakeCase(decl.name)};
|
||||
}
|
||||
}
|
||||
|
||||
const EventTag = @Enum(u16, .exhaustive, names, indices[0..names.len]);
|
||||
break :blk @Union(.auto, EventTag, names, types[0..names.len], &@splat(.{}));
|
||||
};
|
||||
|
||||
inline fn toSnakeCase(comptime name: []const u8) []const u8 {
|
||||
var result: []const u8 = "";
|
||||
|
||||
for (name, 0..) |c, i| {
|
||||
if (std.ascii.isUpper(c)) {
|
||||
if (i != 0) result = result ++ "_";
|
||||
result = result ++ .{std.ascii.toLower(c)};
|
||||
} else result = result ++ .{c};
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
16
gamesv/src/logic/event/kinds.zig
Normal file
16
gamesv/src/logic/event/kinds.zig
Normal file
@@ -0,0 +1,16 @@
|
||||
pub const Login = struct {};
|
||||
|
||||
pub const CharBagTeamModified = struct {
|
||||
team_index: usize,
|
||||
modification: enum {
|
||||
set_leader,
|
||||
set_char_team,
|
||||
},
|
||||
};
|
||||
|
||||
pub const SyncSelfScene = struct {
|
||||
reason: enum {
|
||||
entrance,
|
||||
team_modified,
|
||||
},
|
||||
};
|
||||
116
gamesv/src/logic/messaging.zig
Normal file
116
gamesv/src/logic/messaging.zig
Normal file
@@ -0,0 +1,116 @@
|
||||
const std = @import("std");
|
||||
const proto = @import("proto");
|
||||
const logic = @import("../logic.zig");
|
||||
const network = @import("../network.zig");
|
||||
const Session = @import("../Session.zig");
|
||||
|
||||
const Io = std.Io;
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
const log = std.log.scoped(.messaging);
|
||||
const meta = std.meta;
|
||||
|
||||
const namespaces = &.{
|
||||
@import("messaging/player.zig"),
|
||||
@import("messaging/scene.zig"),
|
||||
@import("messaging/char_bag.zig"),
|
||||
@import("messaging/friend_chat.zig"),
|
||||
};
|
||||
|
||||
pub fn Request(comptime CSType: type) type {
|
||||
return struct {
|
||||
pub const CSMessage = CSType;
|
||||
|
||||
message: *const CSMessage,
|
||||
session: *Session,
|
||||
};
|
||||
}
|
||||
|
||||
const MsgID = blk: {
|
||||
var msg_types: []const type = &.{};
|
||||
|
||||
for (namespaces) |namespace| {
|
||||
for (@typeInfo(namespace).@"struct".decls) |decl_info| {
|
||||
const decl = @field(namespace, decl_info.name);
|
||||
const fn_info = switch (@typeInfo(@TypeOf(decl))) {
|
||||
.@"fn" => |info| info,
|
||||
else => continue,
|
||||
};
|
||||
|
||||
if (fn_info.params.len == 0) continue;
|
||||
const Param = fn_info.params[0].type.?;
|
||||
if (!@hasDecl(Param, "CSMessage")) continue;
|
||||
|
||||
msg_types = msg_types ++ .{Param.CSMessage};
|
||||
}
|
||||
}
|
||||
|
||||
var msg_names: [msg_types.len][]const u8 = @splat("");
|
||||
var msg_ids: [msg_types.len]i32 = @splat(0);
|
||||
|
||||
for (msg_types, 0..) |CSMsg, i| {
|
||||
// Proven to exist by the code above.
|
||||
msg_names[i] = CSMsg.message_name;
|
||||
msg_ids[i] = @intFromEnum(proto.messageId(CSMsg));
|
||||
}
|
||||
|
||||
break :blk @Enum(i32, .exhaustive, &msg_names, &msg_ids);
|
||||
};
|
||||
|
||||
pub fn process(
|
||||
gpa: Allocator,
|
||||
world: *logic.World,
|
||||
request: *const network.Request,
|
||||
) !void {
|
||||
const recv_msg_id = std.enums.fromInt(MsgID, request.head.msgid) orelse {
|
||||
return error.MissingHandler;
|
||||
};
|
||||
|
||||
switch (recv_msg_id) {
|
||||
inline else => |msg_id| {
|
||||
handler_lookup: inline for (namespaces) |namespace| {
|
||||
inline for (@typeInfo(namespace).@"struct".decls) |decl_info| {
|
||||
const decl = @field(namespace, decl_info.name);
|
||||
const fn_info = switch (@typeInfo(@TypeOf(decl))) {
|
||||
.@"fn" => |info| info,
|
||||
else => continue,
|
||||
};
|
||||
|
||||
if (fn_info.params.len == 0) continue;
|
||||
const Param = fn_info.params[0].type.?;
|
||||
if (!@hasDecl(Param, "CSMessage")) continue;
|
||||
|
||||
if (comptime !std.mem.eql(u8, @tagName(msg_id), Param.CSMessage.message_name))
|
||||
continue;
|
||||
|
||||
var arena: std.heap.ArenaAllocator = .init(gpa);
|
||||
defer arena.deinit();
|
||||
|
||||
var queue: logic.event.Queue = .init(arena.allocator());
|
||||
|
||||
var reader: Io.Reader = .fixed(request.body);
|
||||
var message = proto.decodeMessage(&reader, arena.allocator(), Param.CSMessage) catch
|
||||
return error.DecodeFailed;
|
||||
|
||||
var handler_args: meta.ArgsTuple(@TypeOf(decl)) = undefined;
|
||||
handler_args[0] = .{
|
||||
.message = &message,
|
||||
.session = world.session,
|
||||
};
|
||||
|
||||
inline for (fn_info.params[1..], 1..) |param, i| {
|
||||
handler_args[i] = logic.queries.resolve(param.type.?, world, &queue, gpa, arena.allocator()) catch {
|
||||
log.err("message handler for '{s}' requires an optional component", .{@typeName(Param.CSMessage)});
|
||||
return;
|
||||
};
|
||||
}
|
||||
|
||||
try @call(.auto, decl, handler_args);
|
||||
try logic.systems.run(world, &queue, gpa, arena.allocator());
|
||||
|
||||
break :handler_lookup;
|
||||
}
|
||||
} else comptime unreachable;
|
||||
},
|
||||
}
|
||||
}
|
||||
119
gamesv/src/logic/messaging/char_bag.zig
Normal file
119
gamesv/src/logic/messaging/char_bag.zig
Normal file
@@ -0,0 +1,119 @@
|
||||
const std = @import("std");
|
||||
const pb = @import("proto").pb;
|
||||
const logic = @import("../../logic.zig");
|
||||
const Assets = @import("../../Assets.zig");
|
||||
|
||||
const event = logic.event;
|
||||
const messaging = logic.messaging;
|
||||
const Player = logic.Player;
|
||||
|
||||
pub fn onCharBagSetTeamLeader(
|
||||
request: messaging.Request(pb.CS_CHAR_BAG_SET_TEAM_LEADER),
|
||||
char_bag: Player.Component(.char_bag),
|
||||
team_modified_tx: event.Sender(.char_bag_team_modified),
|
||||
) !void {
|
||||
const log = std.log.scoped(.char_bag_set_team_leader);
|
||||
|
||||
if ((request.message.team_type orelse .CHAR_BAG_TEAM_TYPE_MAIN) != .CHAR_BAG_TEAM_TYPE_MAIN)
|
||||
return; // 'TEMP' teams are not supported.
|
||||
|
||||
const team_index = std.math.cast(usize, request.message.team_index) orelse {
|
||||
log.err("invalid team index: {d}", .{request.message.team_index});
|
||||
return;
|
||||
};
|
||||
|
||||
const char_index: Player.CharBag.CharIndex = .fromObjectId(
|
||||
request.message.leaderid,
|
||||
);
|
||||
|
||||
char_bag.data.ensureTeamMember(team_index, char_index) catch |err| switch (err) {
|
||||
error.InvalidTeamIndex => {
|
||||
log.err(
|
||||
"team index is out of range! {d}/{d}",
|
||||
.{ team_index, char_bag.data.teams.len },
|
||||
);
|
||||
return;
|
||||
},
|
||||
error.NotTeamMember => {
|
||||
log.err(
|
||||
"character with index {d} is not a member of team {d}",
|
||||
.{ @intFromEnum(char_index), team_index },
|
||||
);
|
||||
return;
|
||||
},
|
||||
};
|
||||
|
||||
const leader_index = &char_bag.data.teams.items(.leader_index)[team_index];
|
||||
|
||||
log.info(
|
||||
"switching leader for team {d} ({d} -> {d})",
|
||||
.{ team_index, leader_index.*, char_index },
|
||||
);
|
||||
|
||||
leader_index.* = char_index;
|
||||
|
||||
try team_modified_tx.send(.{
|
||||
.team_index = team_index,
|
||||
.modification = .set_leader,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn onCharBagSetTeam(
|
||||
request: messaging.Request(pb.CS_CHAR_BAG_SET_TEAM),
|
||||
char_bag: Player.Component(.char_bag),
|
||||
team_modified_tx: event.Sender(.char_bag_team_modified),
|
||||
) !void {
|
||||
const log = std.log.scoped(.char_bag_set_team);
|
||||
|
||||
const team_index = std.math.cast(usize, request.message.team_index) orelse {
|
||||
log.err("invalid team index: {d}", .{request.message.team_index});
|
||||
return;
|
||||
};
|
||||
|
||||
if (request.message.char_team.items.len > Player.CharBag.Team.slots_count) {
|
||||
log.err(
|
||||
"char_team exceeds slots count! {d}/{d}",
|
||||
.{ request.message.char_team.items.len, Player.CharBag.Team.slots_count },
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (std.mem.findScalar(u64, request.message.char_team.items, request.message.leader_id) == null) {
|
||||
log.err("leader_id doesn't present in char_team", .{});
|
||||
return;
|
||||
}
|
||||
|
||||
var new_char_team: Player.CharBag.Team.SlotArray = @splat(.empty);
|
||||
|
||||
for (request.message.char_team.items, 0..) |char_id, i| {
|
||||
if (std.mem.countScalar(u64, request.message.char_team.items, char_id) > 1) {
|
||||
log.err("duplicated character id: {d}", .{char_id});
|
||||
return;
|
||||
}
|
||||
|
||||
const char_index: Player.CharBag.CharIndex = .fromObjectId(char_id);
|
||||
if (@intFromEnum(char_index) >= char_bag.data.chars.len) {
|
||||
log.err("invalid character object id: {d}", .{char_id});
|
||||
return;
|
||||
}
|
||||
|
||||
new_char_team[i] = .fromCharIndex(char_index);
|
||||
}
|
||||
|
||||
const teams_slice = char_bag.data.teams.slice();
|
||||
teams_slice.items(.char_team)[team_index] = new_char_team;
|
||||
teams_slice.items(.leader_index)[team_index] = .fromObjectId(request.message.leader_id);
|
||||
|
||||
try team_modified_tx.send(.{
|
||||
.team_index = team_index,
|
||||
.modification = .set_char_team,
|
||||
});
|
||||
|
||||
try request.session.send(pb.SC_CHAR_BAG_SET_TEAM{
|
||||
.team_type = .CHAR_BAG_TEAM_TYPE_MAIN,
|
||||
.team_index = request.message.team_index,
|
||||
.char_team = request.message.char_team,
|
||||
.scope_name = 1,
|
||||
.leader_id = request.message.leader_id,
|
||||
});
|
||||
}
|
||||
9
gamesv/src/logic/messaging/friend_chat.zig
Normal file
9
gamesv/src/logic/messaging/friend_chat.zig
Normal file
@@ -0,0 +1,9 @@
|
||||
const pb = @import("proto").pb;
|
||||
const logic = @import("../../logic.zig");
|
||||
const messaging = logic.messaging;
|
||||
|
||||
pub fn onCsFriendChatListSimpleSync(
|
||||
request: messaging.Request(pb.CS_FRIEND_CHAT_LIST_SIMPLE_SYNC),
|
||||
) !void {
|
||||
try request.session.send(pb.SC_FRIEND_CHAT_LIST_SIMPLE_SYNC{});
|
||||
}
|
||||
27
gamesv/src/logic/messaging/player.zig
Normal file
27
gamesv/src/logic/messaging/player.zig
Normal file
@@ -0,0 +1,27 @@
|
||||
const pb = @import("proto").pb;
|
||||
const logic = @import("../../logic.zig");
|
||||
const messaging = logic.messaging;
|
||||
|
||||
pub fn onCsPing(
|
||||
request: messaging.Request(pb.CS_PING),
|
||||
timer: *logic.Resource.PingTimer,
|
||||
) !void {
|
||||
timer.last_client_ts = request.message.client_ts;
|
||||
|
||||
try request.session.send(pb.SC_PING{
|
||||
.client_ts = request.message.client_ts,
|
||||
.server_ts = timer.serverTime(),
|
||||
});
|
||||
}
|
||||
|
||||
pub fn onCsFlushSync(
|
||||
request: messaging.Request(pb.CS_FLUSH_SYNC),
|
||||
timer: *logic.Resource.PingTimer,
|
||||
) !void {
|
||||
timer.last_client_ts = request.message.client_ts;
|
||||
|
||||
try request.session.send(pb.SC_FLUSH_SYNC{
|
||||
.client_ts = request.message.client_ts,
|
||||
.server_ts = timer.serverTime(),
|
||||
});
|
||||
}
|
||||
10
gamesv/src/logic/messaging/scene.zig
Normal file
10
gamesv/src/logic/messaging/scene.zig
Normal file
@@ -0,0 +1,10 @@
|
||||
const pb = @import("proto").pb;
|
||||
const logic = @import("../../logic.zig");
|
||||
const messaging = logic.messaging;
|
||||
|
||||
pub fn onSceneLoadFinish(
|
||||
_: messaging.Request(pb.CS_SCENE_LOAD_FINISH),
|
||||
sync_self_scene_tx: logic.event.Sender(.sync_self_scene),
|
||||
) !void {
|
||||
try sync_self_scene_tx.send(.{ .reason = .entrance });
|
||||
}
|
||||
27
gamesv/src/logic/queries.zig
Normal file
27
gamesv/src/logic/queries.zig
Normal file
@@ -0,0 +1,27 @@
|
||||
const std = @import("std");
|
||||
const logic = @import("../logic.zig");
|
||||
|
||||
const meta = std.meta;
|
||||
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
pub fn resolve(
|
||||
comptime Query: type,
|
||||
world: *logic.World,
|
||||
event_queue: *logic.event.Queue,
|
||||
gpa: Allocator,
|
||||
arena: Allocator,
|
||||
) !Query {
|
||||
if (comptime meta.activeTag(@typeInfo(Query)) == .@"struct") {
|
||||
if (@hasDecl(Query, "allocator_kind")) {
|
||||
switch (Query.allocator_kind) {
|
||||
.gpa => return .{ .interface = gpa },
|
||||
.arena => return .{ .interface = arena },
|
||||
}
|
||||
} else if (@hasDecl(Query, "tx_event_kind")) {
|
||||
return .{ .event_queue = event_queue };
|
||||
}
|
||||
}
|
||||
|
||||
return world.getComponentByType(Query);
|
||||
}
|
||||
94
gamesv/src/logic/systems.zig
Normal file
94
gamesv/src/logic/systems.zig
Normal file
@@ -0,0 +1,94 @@
|
||||
const std = @import("std");
|
||||
const logic = @import("../logic.zig");
|
||||
const Session = @import("../Session.zig");
|
||||
|
||||
const meta = std.meta;
|
||||
const event = logic.event;
|
||||
|
||||
const Io = std.Io;
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
const namespaces = &.{
|
||||
@import("systems/base.zig"),
|
||||
@import("systems/game_vars.zig"),
|
||||
@import("systems/unlock.zig"),
|
||||
@import("systems/item_bag.zig"),
|
||||
@import("systems/char_bag.zig"),
|
||||
@import("systems/bitset.zig"),
|
||||
@import("systems/dungeon.zig"),
|
||||
@import("systems/domain_dev.zig"),
|
||||
@import("systems/factory.zig"),
|
||||
@import("systems/stubs.zig"),
|
||||
@import("systems/friend.zig"),
|
||||
@import("systems/scene.zig"),
|
||||
@import("systems/player_saves.zig"),
|
||||
};
|
||||
|
||||
pub const RunSystemsError = Io.Cancelable || Session.SendError || Allocator.Error;
|
||||
|
||||
// Initiate an event frame by triggering one.
|
||||
pub fn triggerEvent(kind: event.Kind, world: *logic.World, gpa: Allocator) RunSystemsError!void {
|
||||
var arena: std.heap.ArenaAllocator = .init(gpa); // Arena for the event frame.
|
||||
defer arena.deinit();
|
||||
|
||||
var queue: event.Queue = .init(arena.allocator());
|
||||
try queue.push(kind);
|
||||
|
||||
try run(world, &queue, gpa, arena.allocator());
|
||||
}
|
||||
|
||||
// Execute the event frame.
|
||||
pub fn run(world: *logic.World, queue: *event.Queue, gpa: Allocator, arena: Allocator) RunSystemsError!void {
|
||||
while (queue.deque.popFront()) |event_kind| {
|
||||
try dispatchEvent(event_kind, world, queue, gpa, arena);
|
||||
}
|
||||
}
|
||||
|
||||
// Process single event of the frame.
|
||||
fn dispatchEvent(
|
||||
kind: event.Kind,
|
||||
world: *logic.World,
|
||||
queue: *event.Queue,
|
||||
gpa: Allocator,
|
||||
arena: Allocator,
|
||||
) RunSystemsError!void {
|
||||
switch (kind) {
|
||||
inline else => |payload, tag| inline for (namespaces) |namespace| {
|
||||
inline for (@typeInfo(namespace).@"struct".decls) |decl_info| {
|
||||
const decl = @field(namespace, decl_info.name);
|
||||
const fn_info = switch (@typeInfo(@TypeOf(decl))) {
|
||||
.@"fn" => |info| info,
|
||||
else => continue,
|
||||
};
|
||||
|
||||
if (fn_info.params.len == 0) continue;
|
||||
|
||||
const Param = fn_info.params[0].type.?;
|
||||
if (!@hasDecl(Param, "rx_event_kind")) continue;
|
||||
if (Param.rx_event_kind != tag) continue;
|
||||
|
||||
try invoke(payload, decl, world, queue, gpa, arena);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn invoke(
|
||||
payload: anytype,
|
||||
decl: anytype,
|
||||
world: *logic.World,
|
||||
queue: *event.Queue,
|
||||
gpa: Allocator,
|
||||
arena: Allocator,
|
||||
) !void {
|
||||
var handler_args: meta.ArgsTuple(@TypeOf(decl)) = undefined;
|
||||
handler_args[0] = .{ .payload = payload };
|
||||
|
||||
inline for (@typeInfo(@TypeOf(decl)).@"fn".params[1..], 1..) |param, i| {
|
||||
handler_args[i] = logic.queries.resolve(param.type.?, world, queue, gpa, arena) catch {
|
||||
return;
|
||||
};
|
||||
}
|
||||
|
||||
try @call(.auto, decl, handler_args);
|
||||
}
|
||||
21
gamesv/src/logic/systems/base.zig
Normal file
21
gamesv/src/logic/systems/base.zig
Normal file
@@ -0,0 +1,21 @@
|
||||
const pb = @import("proto").pb;
|
||||
const logic = @import("../../logic.zig");
|
||||
const Session = @import("../../Session.zig");
|
||||
|
||||
const Player = logic.Player;
|
||||
|
||||
pub fn syncBaseDataOnLogin(
|
||||
rx: logic.event.Receiver(.login),
|
||||
session: *Session,
|
||||
base_comp: Player.Component(.base),
|
||||
) !void {
|
||||
_ = rx;
|
||||
|
||||
try session.send(pb.SC_SYNC_BASE_DATA{
|
||||
.roleid = base_comp.data.role_id,
|
||||
.role_name = base_comp.data.role_name.view(),
|
||||
.level = @intFromEnum(base_comp.data.level),
|
||||
.gender = @enumFromInt(@intFromEnum(base_comp.data.gender)),
|
||||
.short_id = "1",
|
||||
});
|
||||
}
|
||||
27
gamesv/src/logic/systems/bitset.zig
Normal file
27
gamesv/src/logic/systems/bitset.zig
Normal file
@@ -0,0 +1,27 @@
|
||||
const std = @import("std");
|
||||
const pb = @import("proto").pb;
|
||||
const logic = @import("../../logic.zig");
|
||||
const Session = @import("../../Session.zig");
|
||||
|
||||
const Player = logic.Player;
|
||||
|
||||
pub fn syncAllBitset(
|
||||
rx: logic.event.Receiver(.login),
|
||||
session: *Session,
|
||||
bitset: Player.Component(.bitset),
|
||||
) !void {
|
||||
_ = rx;
|
||||
|
||||
var sync_all_bitset: pb.SC_SYNC_ALL_BITSET = .init;
|
||||
var sets_buf: [Player.Bitset.Type.count]pb.BITSET_DATA = undefined;
|
||||
sync_all_bitset.bitset = .initBuffer(&sets_buf);
|
||||
|
||||
for (&bitset.data.sets, 1..) |*set, i| {
|
||||
sync_all_bitset.bitset.appendAssumeCapacity(.{
|
||||
.type = @intCast(i),
|
||||
.value = .{ .items = @constCast(&set.masks) },
|
||||
});
|
||||
}
|
||||
|
||||
try session.send(sync_all_bitset);
|
||||
}
|
||||
100
gamesv/src/logic/systems/char_bag.zig
Normal file
100
gamesv/src/logic/systems/char_bag.zig
Normal file
@@ -0,0 +1,100 @@
|
||||
const std = @import("std");
|
||||
const pb = @import("proto").pb;
|
||||
const logic = @import("../../logic.zig");
|
||||
const Assets = @import("../../Assets.zig");
|
||||
const Session = @import("../../Session.zig");
|
||||
|
||||
const Player = logic.Player;
|
||||
|
||||
pub fn syncCharBag(
|
||||
rx: logic.event.Receiver(.login),
|
||||
assets: *const Assets,
|
||||
session: *Session,
|
||||
char_bag: Player.Component(.char_bag),
|
||||
arena: logic.Resource.Allocator(.arena),
|
||||
) !void {
|
||||
_ = rx;
|
||||
|
||||
var sync_char_bag: pb.SC_SYNC_CHAR_BAG_INFO = .{
|
||||
.curr_team_index = @intCast(char_bag.data.meta.curr_team_index),
|
||||
.temp_team_info = .init,
|
||||
.scope_name = 1,
|
||||
.max_char_team_member_count = comptime @intCast(Player.CharBag.Team.slots_count),
|
||||
};
|
||||
|
||||
const teams = char_bag.data.teams.slice();
|
||||
|
||||
try sync_char_bag.team_info.ensureTotalCapacity(arena.interface, teams.len);
|
||||
const all_team_slots = try arena.interface.alloc([4]u64, teams.len);
|
||||
|
||||
for (
|
||||
0..,
|
||||
teams.items(.name),
|
||||
teams.items(.char_team),
|
||||
teams.items(.leader_index),
|
||||
) |i, name, slots, leader_index| {
|
||||
var char_team: std.ArrayList(u64) = .initBuffer(&all_team_slots[i]);
|
||||
|
||||
for (slots) |slot| if (slot != .empty) {
|
||||
char_team.appendAssumeCapacity(@intFromEnum(slot) + 1);
|
||||
};
|
||||
|
||||
sync_char_bag.team_info.appendAssumeCapacity(.{
|
||||
.team_name = name.view(),
|
||||
.char_team = .{ .items = char_team.items },
|
||||
.leaderid = leader_index.objectId(),
|
||||
});
|
||||
}
|
||||
|
||||
const chars = char_bag.data.chars.slice();
|
||||
try sync_char_bag.char_info.ensureTotalCapacity(arena.interface, chars.len);
|
||||
|
||||
for (0..chars.len) |i| {
|
||||
const index: Player.CharBag.CharIndex = @enumFromInt(i);
|
||||
|
||||
const template_id = assets.numToStr(.char_id, chars.items(.template_id)[i]) orelse continue;
|
||||
const skills = assets.char_skill_map.map.getPtr(template_id).?;
|
||||
|
||||
var char_info: pb.CHAR_INFO = .{
|
||||
.objid = index.objectId(),
|
||||
.templateid = template_id,
|
||||
.char_type = .default_type,
|
||||
.level = chars.items(.level)[i],
|
||||
.exp = chars.items(.exp)[i],
|
||||
.is_dead = chars.items(.is_dead)[i],
|
||||
.weapon_id = chars.items(.weapon_id)[i].instId(),
|
||||
.own_time = chars.items(.own_time)[i],
|
||||
.equip_medicine_id = chars.items(.equip_medicine_id)[i],
|
||||
.potential_level = chars.items(.potential_level)[i],
|
||||
.normal_skill = skills.normal_skill,
|
||||
.battle_info = .{ .hp = chars.items(.hp)[i], .ultimatesp = chars.items(.ultimate_sp)[i] },
|
||||
.skill_info = .{
|
||||
.normal_skill = skills.normal_skill,
|
||||
.combo_skill = skills.combo_skill,
|
||||
.ultimate_skill = skills.ultimate_skill,
|
||||
.disp_normal_attack_skill = skills.attack_skill,
|
||||
},
|
||||
.talent = .{},
|
||||
.battle_mgr_info = .{
|
||||
.msg_generation = @truncate(index.objectId()),
|
||||
.battle_inst_id = @truncate(index.objectId()),
|
||||
.part_inst_info = .{},
|
||||
},
|
||||
.trial_data = .{},
|
||||
};
|
||||
|
||||
try char_info.skill_info.?.level_info.ensureTotalCapacity(arena.interface, skills.all_skills.len);
|
||||
for (skills.all_skills) |name| {
|
||||
char_info.skill_info.?.level_info.appendAssumeCapacity(.{
|
||||
.skill_id = name,
|
||||
.skill_level = 1,
|
||||
.skill_max_level = 1,
|
||||
.skill_enhanced_level = 1,
|
||||
});
|
||||
}
|
||||
|
||||
try sync_char_bag.char_info.append(arena.interface, char_info);
|
||||
}
|
||||
|
||||
try session.send(sync_char_bag);
|
||||
}
|
||||
23
gamesv/src/logic/systems/domain_dev.zig
Normal file
23
gamesv/src/logic/systems/domain_dev.zig
Normal file
@@ -0,0 +1,23 @@
|
||||
const pb = @import("proto").pb;
|
||||
const logic = @import("../../logic.zig");
|
||||
const Assets = @import("../../Assets.zig");
|
||||
const Session = @import("../../Session.zig");
|
||||
|
||||
pub fn syncDomainDevSystem(
|
||||
rx: logic.event.Receiver(.login),
|
||||
session: *Session,
|
||||
assets: *const Assets,
|
||||
arena: logic.Resource.Allocator(.arena),
|
||||
) !void {
|
||||
_ = rx;
|
||||
|
||||
var domain_dev_sync: pb.SC_DOMAIN_DEVELOPMENT_SYSTEM_SYNC = .init;
|
||||
for (assets.table(.domain_data).keys()) |chapter_id| {
|
||||
try domain_dev_sync.domains.append(arena.interface, .{
|
||||
.chapter_id = chapter_id,
|
||||
.dev_degree = .{ .level = 1 },
|
||||
});
|
||||
}
|
||||
|
||||
try session.send(domain_dev_sync);
|
||||
}
|
||||
16
gamesv/src/logic/systems/dungeon.zig
Normal file
16
gamesv/src/logic/systems/dungeon.zig
Normal file
@@ -0,0 +1,16 @@
|
||||
const pb = @import("proto").pb;
|
||||
const logic = @import("../../logic.zig");
|
||||
const Session = @import("../../Session.zig");
|
||||
|
||||
pub fn syncFullDungeonStatus(
|
||||
rx: logic.event.Receiver(.login),
|
||||
session: *Session,
|
||||
) !void {
|
||||
_ = rx;
|
||||
|
||||
// TODO
|
||||
try session.send(pb.SC_SYNC_FULL_DUNGEON_STATUS{
|
||||
.cur_stamina = 200,
|
||||
.max_stamina = 200,
|
||||
});
|
||||
}
|
||||
53
gamesv/src/logic/systems/factory.zig
Normal file
53
gamesv/src/logic/systems/factory.zig
Normal file
@@ -0,0 +1,53 @@
|
||||
const pb = @import("proto").pb;
|
||||
const logic = @import("../../logic.zig");
|
||||
const Assets = @import("../../Assets.zig");
|
||||
const Session = @import("../../Session.zig");
|
||||
|
||||
const default_chapter = "domain_2";
|
||||
|
||||
pub fn syncFactoryData(
|
||||
rx: logic.event.Receiver(.login),
|
||||
session: *Session,
|
||||
assets: *const Assets,
|
||||
arena: logic.Resource.Allocator(.arena),
|
||||
) !void {
|
||||
_ = rx;
|
||||
|
||||
try session.send(pb.SC_FACTORY_SYNC{
|
||||
.stt = .init,
|
||||
.formula_man = .init,
|
||||
.progress_status = .init,
|
||||
});
|
||||
|
||||
var factory_sync_scope: pb.SC_FACTORY_SYNC_SCOPE = .{
|
||||
.scope_name = 1,
|
||||
.current_chapter_id = default_chapter,
|
||||
.transport_route = .init,
|
||||
.book_mark = .init,
|
||||
.sign_mgr = .init,
|
||||
.shared_mgr = .init,
|
||||
};
|
||||
|
||||
for (assets.table(.domain_data).keys()) |chapter_id| {
|
||||
try factory_sync_scope.active_chapter_ids.append(arena.interface, chapter_id);
|
||||
}
|
||||
|
||||
try session.send(factory_sync_scope);
|
||||
|
||||
for (assets.table(.domain_data).keys()) |chapter_id| {
|
||||
try session.send(pb.SC_FACTORY_SYNC_CHAPTER{
|
||||
.chapter_id = chapter_id,
|
||||
.blackboard = .{ .power = .{ .is_stop_by_power = true } },
|
||||
.pin_board = .{},
|
||||
.statistic = .{},
|
||||
.pending_place = .{},
|
||||
});
|
||||
|
||||
try session.send(pb.SC_FACTORY_HS{
|
||||
.blackboard = .{
|
||||
.power = .{ .is_stop_by_power = true },
|
||||
},
|
||||
.chapter_id = chapter_id,
|
||||
});
|
||||
}
|
||||
}
|
||||
19
gamesv/src/logic/systems/friend.zig
Normal file
19
gamesv/src/logic/systems/friend.zig
Normal file
@@ -0,0 +1,19 @@
|
||||
const pb = @import("proto").pb;
|
||||
const logic = @import("../../logic.zig");
|
||||
const Session = @import("../../Session.zig");
|
||||
|
||||
pub fn syncPersonalData(
|
||||
rx: logic.event.Receiver(.login),
|
||||
session: *Session,
|
||||
) !void {
|
||||
_ = rx;
|
||||
|
||||
// TODO
|
||||
try session.send(pb.SC_FRIEND_PERSONAL_DATA_SYNC{
|
||||
.data = .{
|
||||
.user_avatar_id = 7,
|
||||
.User_avatar_frame_id = 3,
|
||||
.business_card_topic_id = 11,
|
||||
},
|
||||
});
|
||||
}
|
||||
32
gamesv/src/logic/systems/game_vars.zig
Normal file
32
gamesv/src/logic/systems/game_vars.zig
Normal file
@@ -0,0 +1,32 @@
|
||||
const pb = @import("proto").pb;
|
||||
const logic = @import("../../logic.zig");
|
||||
const Session = @import("../../Session.zig");
|
||||
|
||||
const Player = logic.Player;
|
||||
|
||||
pub fn syncAllGameVars(
|
||||
rx: logic.event.Receiver(.login),
|
||||
session: *Session,
|
||||
game_vars: Player.Component(.game_vars),
|
||||
arena: logic.Resource.Allocator(.arena),
|
||||
) !void {
|
||||
_ = rx;
|
||||
|
||||
var sync_all_game_var: pb.SC_SYNC_ALL_GAME_VAR = .init;
|
||||
try sync_all_game_var.server_vars.ensureTotalCapacity(arena.interface, game_vars.data.server_vars.len);
|
||||
try sync_all_game_var.client_vars.ensureTotalCapacity(arena.interface, game_vars.data.client_vars.len);
|
||||
|
||||
for (game_vars.data.server_vars) |sv| {
|
||||
sync_all_game_var.server_vars.appendAssumeCapacity(
|
||||
.{ .key = @intFromEnum(sv.key), .value = sv.value },
|
||||
);
|
||||
}
|
||||
|
||||
for (game_vars.data.client_vars) |cv| {
|
||||
sync_all_game_var.client_vars.appendAssumeCapacity(
|
||||
.{ .key = cv.key, .value = cv.value },
|
||||
);
|
||||
}
|
||||
|
||||
try session.send(sync_all_game_var);
|
||||
}
|
||||
55
gamesv/src/logic/systems/item_bag.zig
Normal file
55
gamesv/src/logic/systems/item_bag.zig
Normal file
@@ -0,0 +1,55 @@
|
||||
const pb = @import("proto").pb;
|
||||
const logic = @import("../../logic.zig");
|
||||
const Session = @import("../../Session.zig");
|
||||
|
||||
const Player = logic.Player;
|
||||
|
||||
pub fn syncItemBagScopes(
|
||||
rx: logic.event.Receiver(.login),
|
||||
session: *Session,
|
||||
item_bag: Player.Component(.item_bag),
|
||||
char_bag: Player.Component(.char_bag),
|
||||
arena: logic.Resource.Allocator(.arena),
|
||||
) !void {
|
||||
_ = rx;
|
||||
|
||||
var item_bag_scope_sync: pb.SC_ITEM_BAG_SCOPE_SYNC = .{
|
||||
.bag = .init,
|
||||
.quick_bar = .init,
|
||||
.assistant = .init,
|
||||
.scope_name = 1,
|
||||
};
|
||||
|
||||
var weapon_depot: pb.SCD_ITEM_DEPOT = .init;
|
||||
try weapon_depot.inst_list.ensureTotalCapacity(arena.interface, item_bag.data.weapon_depot.len);
|
||||
|
||||
const weapon_slice = item_bag.data.weapon_depot.slice();
|
||||
|
||||
for (0..weapon_slice.len) |i| {
|
||||
const weapon_index: Player.ItemBag.WeaponIndex = @enumFromInt(i);
|
||||
const weapon = weapon_slice.get(i);
|
||||
|
||||
weapon_depot.inst_list.appendAssumeCapacity(.{
|
||||
.count = 1,
|
||||
.inst = .{
|
||||
.inst_id = weapon_index.instId(),
|
||||
.inst_impl = .{ .weapon = .{
|
||||
.inst_id = weapon_index.instId(),
|
||||
.template_id = weapon.template_id,
|
||||
.exp = weapon.exp,
|
||||
.weapon_lv = weapon.weapon_lv,
|
||||
.refine_lv = weapon.refine_lv,
|
||||
.breakthrough_lv = weapon.breakthrough_lv,
|
||||
.attach_gem_id = weapon.attach_gem_id,
|
||||
.equip_char_id = if (char_bag.data.charIndexWithWeapon(weapon_index)) |char_index|
|
||||
char_index.objectId()
|
||||
else
|
||||
0,
|
||||
} },
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
try item_bag_scope_sync.depot.append(arena.interface, .{ .key = 1, .value = weapon_depot });
|
||||
try session.send(item_bag_scope_sync);
|
||||
}
|
||||
38
gamesv/src/logic/systems/player_saves.zig
Normal file
38
gamesv/src/logic/systems/player_saves.zig
Normal file
@@ -0,0 +1,38 @@
|
||||
const std = @import("std");
|
||||
const pb = @import("proto").pb;
|
||||
const logic = @import("../../logic.zig");
|
||||
const Session = @import("../../Session.zig");
|
||||
const fs = @import("../../fs.zig");
|
||||
|
||||
const Io = std.Io;
|
||||
const Player = logic.Player;
|
||||
|
||||
const log = std.log.scoped(.player_saves);
|
||||
|
||||
pub fn saveCharBagTeams(
|
||||
_: logic.event.Receiver(.char_bag_team_modified),
|
||||
char_bag: Player.Component(.char_bag),
|
||||
player_id: logic.World.PlayerId,
|
||||
io: Io,
|
||||
) !void {
|
||||
const data_dir = fs.persistence.openPlayerDataDir(io, player_id.uid) catch |err| switch (err) {
|
||||
error.Canceled => |e| return e,
|
||||
else => |e| {
|
||||
log.err(
|
||||
"failed to open data dir for player with uid {d}: {t}",
|
||||
.{ player_id.uid, e },
|
||||
);
|
||||
return;
|
||||
},
|
||||
};
|
||||
|
||||
defer data_dir.close(io);
|
||||
|
||||
fs.persistence.saveCharBagComponent(io, data_dir, char_bag.data, .teams) catch |err| switch (err) {
|
||||
error.Canceled => |e| return e,
|
||||
else => |e| {
|
||||
log.err("save failed: {t}", .{e});
|
||||
return;
|
||||
},
|
||||
};
|
||||
}
|
||||
180
gamesv/src/logic/systems/scene.zig
Normal file
180
gamesv/src/logic/systems/scene.zig
Normal file
@@ -0,0 +1,180 @@
|
||||
const std = @import("std");
|
||||
const pb = @import("proto").pb;
|
||||
const logic = @import("../../logic.zig");
|
||||
const Session = @import("../../Session.zig");
|
||||
const Assets = @import("../../Assets.zig");
|
||||
|
||||
const Player = logic.Player;
|
||||
const ArrayList = std.ArrayList;
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
const default_level = "map02_lv001";
|
||||
|
||||
pub fn enterSceneOnLogin(
|
||||
rx: logic.event.Receiver(.login),
|
||||
session: *Session,
|
||||
assets: *const Assets,
|
||||
base_comp: Player.Component(.base),
|
||||
) !void {
|
||||
_ = rx;
|
||||
|
||||
const level_config = assets.level_config_table.getPtr(default_level).?;
|
||||
const position: pb.VECTOR = .{
|
||||
.X = level_config.playerInitPos.x,
|
||||
.Y = level_config.playerInitPos.y,
|
||||
.Z = level_config.playerInitPos.z,
|
||||
};
|
||||
|
||||
try session.send(pb.SC_CHANGE_SCENE_BEGIN_NOTIFY{
|
||||
.scene_num_id = level_config.idNum,
|
||||
.position = position,
|
||||
.pass_through_data = .init,
|
||||
});
|
||||
|
||||
try session.send(pb.SC_ENTER_SCENE_NOTIFY{
|
||||
.role_id = base_comp.data.role_id,
|
||||
.scene_num_id = level_config.idNum,
|
||||
.position = position,
|
||||
.pass_through_data = .init,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn refreshCharTeam(
|
||||
rx: logic.event.Receiver(.char_bag_team_modified),
|
||||
char_bag: Player.Component(.char_bag),
|
||||
sync_tx: logic.event.Sender(.sync_self_scene),
|
||||
) !void {
|
||||
switch (rx.payload.modification) {
|
||||
.set_leader => return, // Doesn't require any action from server.
|
||||
.set_char_team => if (rx.payload.team_index == char_bag.data.meta.curr_team_index) {
|
||||
// If the current active team has been modified, it has to be re-spawned.
|
||||
try sync_tx.send(.{ .reason = .team_modified });
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn syncSelfScene(
|
||||
rx: logic.event.Receiver(.sync_self_scene),
|
||||
session: *Session,
|
||||
arena: logic.Resource.Allocator(.arena),
|
||||
char_bag: logic.Player.Component(.char_bag),
|
||||
assets: *const Assets,
|
||||
) !void {
|
||||
const reason: pb.SELF_INFO_REASON_TYPE = switch (rx.payload.reason) {
|
||||
.entrance => .SLR_ENTER_SCENE,
|
||||
.team_modified => .SLR_CHANGE_TEAM,
|
||||
};
|
||||
|
||||
const level_config = assets.level_config_table.getPtr(default_level).?;
|
||||
const position: pb.VECTOR = .{
|
||||
.X = level_config.playerInitPos.x,
|
||||
.Y = level_config.playerInitPos.y,
|
||||
.Z = level_config.playerInitPos.z,
|
||||
};
|
||||
|
||||
const team_index = char_bag.data.meta.curr_team_index;
|
||||
const leader_index = char_bag.data.teams.items(.leader_index)[team_index];
|
||||
|
||||
var self_scene_info: pb.SC_SELF_SCENE_INFO = .{
|
||||
.scene_num_id = level_config.idNum,
|
||||
.self_info_reason = @intFromEnum(reason),
|
||||
.teamInfo = .{
|
||||
.team_type = .CHAR_BAG_TEAM_TYPE_MAIN,
|
||||
.team_index = @intCast(team_index),
|
||||
.cur_leader_id = leader_index.objectId(),
|
||||
.team_change_token = 0,
|
||||
},
|
||||
.scene_impl = .{ .empty = .{} },
|
||||
.detail = .{},
|
||||
};
|
||||
|
||||
for (char_bag.data.teams.items(.char_team)[team_index]) |slot| {
|
||||
const char_index = slot.charIndex() orelse continue;
|
||||
const char_template_id_num = char_bag.data.chars.items(.template_id)[@intFromEnum(char_index)];
|
||||
const char_template_id = assets.numToStr(.char_id, char_template_id_num).?;
|
||||
const char_data = assets.table(.character).getPtr(char_template_id).?;
|
||||
|
||||
var scene_char: pb.SCENE_CHARACTER = .{
|
||||
.level = 1,
|
||||
.battle_info = .{
|
||||
.msg_generation = @intCast(char_index.objectId()),
|
||||
.battle_inst_id = @intCast(char_index.objectId()),
|
||||
.part_inst_info = .{},
|
||||
},
|
||||
.common_info = .{
|
||||
.id = char_index.objectId(),
|
||||
.templateid = char_template_id,
|
||||
.position = position,
|
||||
.rotation = .{},
|
||||
.scene_num_id = level_config.idNum,
|
||||
},
|
||||
};
|
||||
|
||||
for (char_data.attributes[0].Attribute.attrs) |attr| {
|
||||
if (attr.attrType == .max_hp)
|
||||
scene_char.common_info.?.hp = attr.attrValue;
|
||||
|
||||
try scene_char.attrs.append(arena.interface, .{
|
||||
.attr_type = @intFromEnum(attr.attrType),
|
||||
.basic_value = attr.attrValue,
|
||||
.value = attr.attrValue,
|
||||
});
|
||||
}
|
||||
|
||||
scene_char.battle_info.?.skill_list = try packCharacterSkills(
|
||||
arena.interface,
|
||||
assets,
|
||||
char_template_id,
|
||||
);
|
||||
|
||||
try self_scene_info.detail.?.char_list.append(arena.interface, scene_char);
|
||||
}
|
||||
|
||||
try session.send(self_scene_info);
|
||||
}
|
||||
|
||||
fn packCharacterSkills(
|
||||
arena: Allocator,
|
||||
assets: *const Assets,
|
||||
template_id: []const u8,
|
||||
) Allocator.Error!ArrayList(pb.SERVER_SKILL) {
|
||||
const char_skills = assets.char_skill_map.map.getPtr(template_id).?.all_skills;
|
||||
var list: ArrayList(pb.SERVER_SKILL) = try .initCapacity(
|
||||
arena,
|
||||
char_skills.len + assets.common_skill_config.config.Character.skillConfigs.len,
|
||||
);
|
||||
|
||||
errdefer comptime unreachable;
|
||||
|
||||
for (char_skills, 1..) |name, i| {
|
||||
list.appendAssumeCapacity(.{
|
||||
.skill_id = .{
|
||||
.id_impl = .{ .str_id = name },
|
||||
.type = .BATTLE_ACTION_OWNER_TYPE_SKILL,
|
||||
},
|
||||
.blackboard = .{},
|
||||
.inst_id = (100 + i),
|
||||
.level = 1,
|
||||
.source = .BATTLE_SKILL_SOURCE_DEFAULT,
|
||||
.potential_lv = 1,
|
||||
.is_enable = true,
|
||||
});
|
||||
}
|
||||
|
||||
for (assets.common_skill_config.config.Character.skillConfigs, char_skills.len + 1..) |config, i| {
|
||||
list.appendAssumeCapacity(.{
|
||||
.skill_id = .{
|
||||
.id_impl = .{ .str_id = config.skillId },
|
||||
.type = .BATTLE_ACTION_OWNER_TYPE_SKILL,
|
||||
},
|
||||
.blackboard = .{},
|
||||
.inst_id = (100 + i),
|
||||
.level = 1,
|
||||
.source = .BATTLE_SKILL_SOURCE_DEFAULT,
|
||||
.potential_lv = 1,
|
||||
.is_enable = true,
|
||||
});
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
50
gamesv/src/logic/systems/stubs.zig
Normal file
50
gamesv/src/logic/systems/stubs.zig
Normal file
@@ -0,0 +1,50 @@
|
||||
const pb = @import("proto").pb;
|
||||
const logic = @import("../../logic.zig");
|
||||
const Session = @import("../../Session.zig");
|
||||
|
||||
// Sends the dummy 'SYNC' messages for the components that aren't implemented yet.
|
||||
pub fn loginSyncStub(
|
||||
rx: logic.event.Receiver(.login),
|
||||
session: *Session,
|
||||
) !void {
|
||||
_ = rx;
|
||||
|
||||
try session.send(pb.SC_ADVENTURE_SYNC_ALL{
|
||||
.level = 1,
|
||||
.world_level = 1,
|
||||
.unlock_world_level = 1,
|
||||
});
|
||||
|
||||
try session.send(pb.SC_ADVENTURE_BOOK_SYNC{
|
||||
.adventure_book_stage = 1,
|
||||
});
|
||||
|
||||
try session.send(pb.SC_SYNC_ALL_MINI_GAME.init);
|
||||
try session.send(pb.SC_SYNC_ALL_MAIL.init);
|
||||
try session.send(pb.SC_KITE_STATION_SYNC_ALL.init);
|
||||
try session.send(pb.SC_SYNC_ALL_GUIDE.init);
|
||||
try session.send(pb.SC_GLOBAL_EFFECT_SYNC_ALL.init);
|
||||
try session.send(pb.SC_SYNC_ALL_DOODAD_GROUP.init);
|
||||
try session.send(pb.SC_SETTLEMENT_SYNC_ALL.init);
|
||||
try session.send(pb.SC_DOMAIN_DEPOT_SYNC_ALL_INFO.init);
|
||||
try session.send(pb.SC_SYNC_ALL_DIALOG.init);
|
||||
try session.send(pb.SC_SYNC_ALL_ROLE_SCENE.init);
|
||||
try session.send(pb.SC_SYNC_ALL_WIKI.init);
|
||||
try session.send(pb.SC_RECYCLE_BIN_SYSTEM_SYNC_ALL.init);
|
||||
try session.send(pb.SC_SYNC_ALL_STAT.init);
|
||||
try session.send(pb.SC_BP_SYNC_ALL{
|
||||
.season_data = .init,
|
||||
.level_data = .init,
|
||||
.bp_track_mgr = .init,
|
||||
.bp_task_mgr = .init,
|
||||
});
|
||||
try session.send(pb.SC_SYNC_ALL_MISSION.init);
|
||||
try session.send(pb.SC_SPACESHIP_SYNC{
|
||||
.assist_data = .init,
|
||||
.expedition_data = .init,
|
||||
});
|
||||
|
||||
try session.send(pb.SC_ACHIEVE_SYNC{
|
||||
.achieve_display_info = .init,
|
||||
});
|
||||
}
|
||||
22
gamesv/src/logic/systems/unlock.zig
Normal file
22
gamesv/src/logic/systems/unlock.zig
Normal file
@@ -0,0 +1,22 @@
|
||||
const pb = @import("proto").pb;
|
||||
const logic = @import("../../logic.zig");
|
||||
const Session = @import("../../Session.zig");
|
||||
|
||||
const Player = logic.Player;
|
||||
|
||||
pub fn syncAllUnlock(
|
||||
rx: logic.event.Receiver(.login),
|
||||
session: *Session,
|
||||
unlock: Player.Component(.unlock),
|
||||
arena: logic.Resource.Allocator(.arena),
|
||||
) !void {
|
||||
_ = rx;
|
||||
|
||||
var sync_all_unlock: pb.SC_SYNC_ALL_UNLOCK = .init;
|
||||
try sync_all_unlock.unlock_systems.appendSlice(
|
||||
arena.interface,
|
||||
@ptrCast(unlock.data.unlocked_systems),
|
||||
);
|
||||
|
||||
try session.send(sync_all_unlock);
|
||||
}
|
||||
Reference in New Issue
Block a user