mirror of
https://git.xeondev.com/LR/S.git
synced 2026-02-04 15:05:17 +01:00
Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
23f9797a8a | ||
|
|
66dff8ddb1 | ||
|
|
3acc274a7d | ||
|
|
f0c3f57602 | ||
|
|
5dff70b504 | ||
|
|
f96fb51e3c | ||
|
|
7a91e1e975 | ||
|
|
b5012c03f4 | ||
|
|
3b20a381fa | ||
|
|
277d5f5573 | ||
|
|
104fcf4b95 |
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
# Getting Started
|
# Getting Started
|
||||||
## Requirements
|
## Requirements
|
||||||
- Zig 0.16.0-dev.2368: [Linux](https://ziglang.org/builds/zig-x86_64-linux-0.16.0-dev.2368+380ea6fb5.tar.xz)/[Windows](https://ziglang.org/builds/zig-x86_64-windows-0.16.0-dev.2368+380ea6fb5.zip)
|
- Zig 0.16.0-dev.2459: [Linux](https://ziglang.org/builds/zig-x86_64-linux-0.16.0-dev.2459+37d14a4f3.tar.xz)/[Windows](https://ziglang.org/builds/zig-x86_64-windows-0.16.0-dev.2459+37d14a4f3.zip)
|
||||||
|
|
||||||
#### For additional help, you can join our [discord server](https://discord.xeondev.com)
|
#### For additional help, you can join our [discord server](https://discord.xeondev.com)
|
||||||
|
|
||||||
@@ -33,6 +33,8 @@ Currently supported client version is `1.0.14`, you can get it from 3rd party so
|
|||||||
|
|
||||||
Next, you have to apply the necessary [client patch](https://git.xeondev.com/LR/C). It allows you to connect to the local server.
|
Next, you have to apply the necessary [client patch](https://git.xeondev.com/LR/C). It allows you to connect to the local server.
|
||||||
|
|
||||||
|
Once the patched game is running, you'll see a debug login window with a bunch of random characters. You should replace these with some numeric UID (digits only). After that, you can login normally.
|
||||||
|
|
||||||
## Community
|
## Community
|
||||||
- [Our Discord Server](https://discord.xeondev.com)
|
- [Our Discord Server](https://discord.xeondev.com)
|
||||||
- [Our Telegram Channel](https://t.me/reversedrooms)
|
- [Our Telegram Channel](https://t.me/reversedrooms)
|
||||||
|
|||||||
49132
assets/configs/LevelMapMark.json
Normal file
49132
assets/configs/LevelMapMark.json
Normal file
File diff suppressed because it is too large
Load Diff
2047
assets/configs/MapTeleportValidationDataTable.json
Normal file
2047
assets/configs/MapTeleportValidationDataTable.json
Normal file
File diff suppressed because it is too large
Load Diff
327
assets/tables/TrackMapPointTable.json
Normal file
327
assets/tables/TrackMapPointTable.json
Normal file
@@ -0,0 +1,327 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"Key": "1",
|
||||||
|
"Value": {
|
||||||
|
"end": "map01_lv002",
|
||||||
|
"pos": {
|
||||||
|
"x": -660.3,
|
||||||
|
"y": 119.32,
|
||||||
|
"z": -126.9
|
||||||
|
},
|
||||||
|
"quest": "",
|
||||||
|
"start": "map01_lv001"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Key": "24",
|
||||||
|
"Value": {
|
||||||
|
"end": "map02_lv004",
|
||||||
|
"pos": {
|
||||||
|
"x": 118.47,
|
||||||
|
"y": 137.65,
|
||||||
|
"z": 0.5
|
||||||
|
},
|
||||||
|
"quest": "",
|
||||||
|
"start": "indie_dg007"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Key": "11",
|
||||||
|
"Value": {
|
||||||
|
"end": "map02_lv002",
|
||||||
|
"pos": {
|
||||||
|
"x": -961.8,
|
||||||
|
"y": 263.46,
|
||||||
|
"z": -895.33
|
||||||
|
},
|
||||||
|
"quest": "e6m5_q#3",
|
||||||
|
"start": "map02_lv001"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Key": "15",
|
||||||
|
"Value": {
|
||||||
|
"end": "map02_lv002",
|
||||||
|
"pos": {
|
||||||
|
"x": -1711.17,
|
||||||
|
"y": 408.86,
|
||||||
|
"z": -338.58
|
||||||
|
},
|
||||||
|
"quest": "",
|
||||||
|
"start": "indie_dg005"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Key": "3",
|
||||||
|
"Value": {
|
||||||
|
"end": "map01_lv005",
|
||||||
|
"pos": {
|
||||||
|
"x": 255.0,
|
||||||
|
"y": 99.0,
|
||||||
|
"z": -340.0
|
||||||
|
},
|
||||||
|
"quest": "",
|
||||||
|
"start": "map01_lv001"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Key": "14",
|
||||||
|
"Value": {
|
||||||
|
"end": "map02_lv001",
|
||||||
|
"pos": {
|
||||||
|
"x": -718.6,
|
||||||
|
"y": 256.91,
|
||||||
|
"z": -896.89
|
||||||
|
},
|
||||||
|
"quest": "",
|
||||||
|
"start": "map02_lv002"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Key": "2",
|
||||||
|
"Value": {
|
||||||
|
"end": "map01_lv003",
|
||||||
|
"pos": {
|
||||||
|
"x": 129.0,
|
||||||
|
"y": 73.0,
|
||||||
|
"z": -567.0
|
||||||
|
},
|
||||||
|
"quest": "",
|
||||||
|
"start": "map01_lv001"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Key": "16",
|
||||||
|
"Value": {
|
||||||
|
"end": "indie_dg005",
|
||||||
|
"pos": {
|
||||||
|
"x": -1443.57,
|
||||||
|
"y": 324.2216,
|
||||||
|
"z": -336.5857
|
||||||
|
},
|
||||||
|
"quest": "",
|
||||||
|
"start": "map02_lv002"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Key": "25",
|
||||||
|
"Value": {
|
||||||
|
"end": "map02_lv001",
|
||||||
|
"pos": {
|
||||||
|
"x": -1151.73,
|
||||||
|
"y": 206.17,
|
||||||
|
"z": -1355.25
|
||||||
|
},
|
||||||
|
"quest": "",
|
||||||
|
"start": "map02_lv004"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Key": "13",
|
||||||
|
"Value": {
|
||||||
|
"end": "map02_lv002",
|
||||||
|
"pos": {
|
||||||
|
"x": -719.07,
|
||||||
|
"y": 257.85,
|
||||||
|
"z": -895.02
|
||||||
|
},
|
||||||
|
"quest": "",
|
||||||
|
"start": "map02_lv001"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Key": "9",
|
||||||
|
"Value": {
|
||||||
|
"end": "map01_lv007",
|
||||||
|
"pos": {
|
||||||
|
"x": 127.0,
|
||||||
|
"y": 93.5,
|
||||||
|
"z": 711.0
|
||||||
|
},
|
||||||
|
"quest": "",
|
||||||
|
"start": "map01_lv006"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Key": "23",
|
||||||
|
"Value": {
|
||||||
|
"end": "indie_dg007",
|
||||||
|
"pos": {
|
||||||
|
"x": -1087.61,
|
||||||
|
"y": 38.05,
|
||||||
|
"z": -864.19
|
||||||
|
},
|
||||||
|
"quest": "",
|
||||||
|
"start": "map02_lv004"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Key": "8",
|
||||||
|
"Value": {
|
||||||
|
"end": "map01_lv005",
|
||||||
|
"pos": {
|
||||||
|
"x": 682.0,
|
||||||
|
"y": 54.0,
|
||||||
|
"z": -1.0
|
||||||
|
},
|
||||||
|
"quest": "",
|
||||||
|
"start": "map01_lv006"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Key": "19",
|
||||||
|
"Value": {
|
||||||
|
"end": "map02_lv004",
|
||||||
|
"pos": {
|
||||||
|
"x": -1384.38,
|
||||||
|
"y": 276.66,
|
||||||
|
"z": -896.44
|
||||||
|
},
|
||||||
|
"quest": "",
|
||||||
|
"start": "map02_lv002"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Key": "17",
|
||||||
|
"Value": {
|
||||||
|
"end": "map02_lv003",
|
||||||
|
"pos": {
|
||||||
|
"x": -529.4,
|
||||||
|
"y": 262.25,
|
||||||
|
"z": -1024.36
|
||||||
|
},
|
||||||
|
"quest": "",
|
||||||
|
"start": "map02_lv001"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Key": "10",
|
||||||
|
"Value": {
|
||||||
|
"end": "map01_lv006",
|
||||||
|
"pos": {
|
||||||
|
"x": 129.0,
|
||||||
|
"y": 93.5,
|
||||||
|
"z": 714.0
|
||||||
|
},
|
||||||
|
"quest": "",
|
||||||
|
"start": "map01_lv007"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Key": "20",
|
||||||
|
"Value": {
|
||||||
|
"end": "map02_lv002",
|
||||||
|
"pos": {
|
||||||
|
"x": -1383.36,
|
||||||
|
"y": 278.66,
|
||||||
|
"z": -895.34
|
||||||
|
},
|
||||||
|
"quest": "",
|
||||||
|
"start": "map02_lv004"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Key": "18",
|
||||||
|
"Value": {
|
||||||
|
"end": "map02_lv001",
|
||||||
|
"pos": {
|
||||||
|
"x": -529.7,
|
||||||
|
"y": 262.25,
|
||||||
|
"z": -1023.57
|
||||||
|
},
|
||||||
|
"quest": "",
|
||||||
|
"start": "map02_lv003"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Key": "22",
|
||||||
|
"Value": {
|
||||||
|
"end": "map02_lv002",
|
||||||
|
"pos": {
|
||||||
|
"x": -640.41,
|
||||||
|
"y": 253.97,
|
||||||
|
"z": -326.63
|
||||||
|
},
|
||||||
|
"quest": "",
|
||||||
|
"start": "map02_lv005"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Key": "5",
|
||||||
|
"Value": {
|
||||||
|
"end": "map01_lv001",
|
||||||
|
"pos": {
|
||||||
|
"x": 127.0,
|
||||||
|
"y": 73.0,
|
||||||
|
"z": -567.0
|
||||||
|
},
|
||||||
|
"quest": "",
|
||||||
|
"start": "map01_lv003"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Key": "4",
|
||||||
|
"Value": {
|
||||||
|
"end": "map01_lv001",
|
||||||
|
"pos": {
|
||||||
|
"x": -656.74,
|
||||||
|
"y": 119.62,
|
||||||
|
"z": -129.1
|
||||||
|
},
|
||||||
|
"quest": "",
|
||||||
|
"start": "map01_lv002"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Key": "21",
|
||||||
|
"Value": {
|
||||||
|
"end": "map02_lv005",
|
||||||
|
"pos": {
|
||||||
|
"x": -639.63,
|
||||||
|
"y": 253.97,
|
||||||
|
"z": -326.32
|
||||||
|
},
|
||||||
|
"quest": "",
|
||||||
|
"start": "map02_lv002"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Key": "7",
|
||||||
|
"Value": {
|
||||||
|
"end": "map01_lv006",
|
||||||
|
"pos": {
|
||||||
|
"x": 682.0,
|
||||||
|
"y": 54.0,
|
||||||
|
"z": 1.0
|
||||||
|
},
|
||||||
|
"quest": "",
|
||||||
|
"start": "map01_lv005"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Key": "12",
|
||||||
|
"Value": {
|
||||||
|
"end": "map02_lv001",
|
||||||
|
"pos": {
|
||||||
|
"x": -961.8,
|
||||||
|
"y": 264.13,
|
||||||
|
"z": -897.53
|
||||||
|
},
|
||||||
|
"quest": "e6m5_q#3",
|
||||||
|
"start": "map02_lv002"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Key": "6",
|
||||||
|
"Value": {
|
||||||
|
"end": "map01_lv001",
|
||||||
|
"pos": {
|
||||||
|
"x": 257.0,
|
||||||
|
"y": 99.0,
|
||||||
|
"z": -341.0
|
||||||
|
},
|
||||||
|
"quest": "",
|
||||||
|
"start": "map01_lv005"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
.{
|
.{
|
||||||
.name = .LR_S,
|
.name = .LR_S,
|
||||||
.version = "0.1.0",
|
.version = "0.1.0",
|
||||||
.minimum_zig_version = "0.16.0-dev.2368+380ea6fb5",
|
.minimum_zig_version = "0.16.0-dev.2471+e9eadee00",
|
||||||
.paths = .{""},
|
.paths = .{""},
|
||||||
.fingerprint = 0x50ff8392fab61337,
|
.fingerprint = 0x50ff8392fab61337,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1721,16 +1721,6 @@ pub fn Poll(comptime io_options: IoOptions) type {
|
|||||||
return tio.vtable.dirCreateDirPathOpen(tio.userdata, dir, sub_path, perm, options);
|
return tio.vtable.dirCreateDirPathOpen(tio.userdata, dir, sub_path, perm, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fileWriteStreaming(userdata: ?*anyopaque, file: Io.File, header: []const u8, data: []const []const u8, splat: usize) Io.File.Writer.Error!usize {
|
|
||||||
const p: *ThisPoll = @ptrCast(@alignCast(userdata));
|
|
||||||
try checkCancel(p);
|
|
||||||
|
|
||||||
var t: Io.Threaded = .init_single_threaded;
|
|
||||||
const tio = t.io();
|
|
||||||
|
|
||||||
return tio.vtable.fileWriteStreaming(tio.userdata, file, header, data, splat);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fileWritePositional(userdata: ?*anyopaque, file: Io.File, header: []const u8, data: []const []const u8, splat: usize, offset: u64) Io.File.WritePositionalError!usize {
|
fn fileWritePositional(userdata: ?*anyopaque, file: Io.File, header: []const u8, data: []const []const u8, splat: usize, offset: u64) Io.File.WritePositionalError!usize {
|
||||||
const p: *ThisPoll = @ptrCast(@alignCast(userdata));
|
const p: *ThisPoll = @ptrCast(@alignCast(userdata));
|
||||||
try checkCancel(p);
|
try checkCancel(p);
|
||||||
@@ -1791,14 +1781,16 @@ pub fn Poll(comptime io_options: IoOptions) type {
|
|||||||
return tio.vtable.fileReadPositional(tio.userdata, file, data, offset);
|
return tio.vtable.fileReadPositional(tio.userdata, file, data, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fileReadStreaming(userdata: ?*anyopaque, file: Io.File, data: []const []u8) Io.File.Reader.Error!usize {
|
fn operate(userdata: ?*anyopaque, operation: Io.Operation) Io.Cancelable!Io.Operation.Result {
|
||||||
|
// TODO: implement network operations once they're migrated to this API.
|
||||||
|
|
||||||
const p: *ThisPoll = @ptrCast(@alignCast(userdata));
|
const p: *ThisPoll = @ptrCast(@alignCast(userdata));
|
||||||
try checkCancel(p);
|
try checkCancel(p);
|
||||||
|
|
||||||
var t: Io.Threaded = .init_single_threaded;
|
var t: Io.Threaded = .init_single_threaded;
|
||||||
const tio = t.io();
|
const tio = t.io();
|
||||||
|
|
||||||
return tio.vtable.fileReadStreaming(tio.userdata, file, data);
|
return tio.vtable.operate(tio.userdata, operation);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn socketClose(socket: posix.socket_t) void {
|
fn socketClose(socket: posix.socket_t) void {
|
||||||
|
|||||||
2
envrc
2
envrc
@@ -1,7 +1,7 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
# DEPENDS: curl, tar, xz, realpath
|
# DEPENDS: curl, tar, xz, realpath
|
||||||
|
|
||||||
ZIG_VERSION="0.16.0-dev.2368+380ea6fb5"
|
ZIG_VERSION="0.16.0-dev.2471+e9eadee00"
|
||||||
ZIG_PLATFORM="x86_64-linux"
|
ZIG_PLATFORM="x86_64-linux"
|
||||||
ZIG_DIST="zig-${ZIG_PLATFORM}-${ZIG_VERSION}"
|
ZIG_DIST="zig-${ZIG_PLATFORM}-${ZIG_VERSION}"
|
||||||
ZIG_DIR="./.direnv/${ZIG_DIST}/"
|
ZIG_DIR="./.direnv/${ZIG_DIST}/"
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ pub const CharacterSkillMap = @import("Assets/CharacterSkillMap.zig");
|
|||||||
const Io = std.Io;
|
const Io = std.Io;
|
||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
const ArenaAllocator = std.heap.ArenaAllocator;
|
const ArenaAllocator = std.heap.ArenaAllocator;
|
||||||
|
const StringHashMap = std.StringArrayHashMapUnmanaged;
|
||||||
|
|
||||||
const meta = std.meta;
|
const meta = std.meta;
|
||||||
const log = std.log.scoped(.assets);
|
const log = std.log.scoped(.assets);
|
||||||
@@ -17,7 +18,12 @@ char_skill_map: CharacterSkillMap,
|
|||||||
str_to_num_dicts: IndexDictionaries.StrToNum,
|
str_to_num_dicts: IndexDictionaries.StrToNum,
|
||||||
num_to_str_dicts: IndexDictionaries.NumToStr,
|
num_to_str_dicts: IndexDictionaries.NumToStr,
|
||||||
common_skill_config: configs.CommonSkillConfig,
|
common_skill_config: configs.CommonSkillConfig,
|
||||||
level_config_table: std.StringArrayHashMapUnmanaged(configs.LevelConfig),
|
level_config_table: StringHashMap(configs.LevelConfig),
|
||||||
|
// Map mark groups as they're stored in LevelMapMark.json
|
||||||
|
level_map_mark_groups: StringHashMap([]const configs.ClientSingleMapMarkData),
|
||||||
|
// instId-to-data mapping
|
||||||
|
map_mark_table: StringHashMap(*const configs.ClientSingleMapMarkData),
|
||||||
|
teleport_validation_table: configs.TeleportValidationDataTable,
|
||||||
|
|
||||||
pub const IdGroup = enum {
|
pub const IdGroup = enum {
|
||||||
char_id,
|
char_id,
|
||||||
@@ -63,11 +69,27 @@ pub fn load(io: Io, gpa: Allocator) !Assets {
|
|||||||
configs.CommonSkillConfig.file,
|
configs.CommonSkillConfig.file,
|
||||||
);
|
);
|
||||||
|
|
||||||
const level_config_table = try configs.loadJsonConfig(
|
const level_config_table = (try configs.loadJsonConfig(
|
||||||
std.json.ArrayHashMap(configs.LevelConfig),
|
std.json.ArrayHashMap(configs.LevelConfig),
|
||||||
io,
|
io,
|
||||||
arena.allocator(),
|
arena.allocator(),
|
||||||
"LevelConfigTable.json",
|
"LevelConfigTable.json",
|
||||||
|
)).map;
|
||||||
|
|
||||||
|
const level_map_mark_groups = (try configs.loadJsonConfig(
|
||||||
|
std.json.ArrayHashMap([]const configs.ClientSingleMapMarkData),
|
||||||
|
io,
|
||||||
|
arena.allocator(),
|
||||||
|
"LevelMapMark.json",
|
||||||
|
)).map;
|
||||||
|
|
||||||
|
const map_mark_table = try buildMapMarkTable(&level_map_mark_groups, arena.allocator());
|
||||||
|
|
||||||
|
const teleport_validation_table = try configs.loadJsonConfig(
|
||||||
|
configs.TeleportValidationDataTable,
|
||||||
|
io,
|
||||||
|
arena.allocator(),
|
||||||
|
"MapTeleportValidationDataTable.json",
|
||||||
);
|
);
|
||||||
|
|
||||||
return .{
|
return .{
|
||||||
@@ -77,10 +99,31 @@ pub fn load(io: Io, gpa: Allocator) !Assets {
|
|||||||
.str_to_num_dicts = str_to_num_dicts,
|
.str_to_num_dicts = str_to_num_dicts,
|
||||||
.num_to_str_dicts = num_to_str_dicts,
|
.num_to_str_dicts = num_to_str_dicts,
|
||||||
.common_skill_config = common_skill_config,
|
.common_skill_config = common_skill_config,
|
||||||
.level_config_table = level_config_table.map,
|
.level_config_table = level_config_table,
|
||||||
|
.level_map_mark_groups = level_map_mark_groups,
|
||||||
|
.map_mark_table = map_mark_table,
|
||||||
|
.teleport_validation_table = teleport_validation_table,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn buildMapMarkTable(
|
||||||
|
groups: *const StringHashMap([]const configs.ClientSingleMapMarkData),
|
||||||
|
arena: Allocator,
|
||||||
|
) Allocator.Error!StringHashMap(*const configs.ClientSingleMapMarkData) {
|
||||||
|
var map: StringHashMap(*const configs.ClientSingleMapMarkData) = .empty;
|
||||||
|
|
||||||
|
for (groups.values()) |group| for (group) |*mark| {
|
||||||
|
const inst_id = try std.mem.concat(
|
||||||
|
arena,
|
||||||
|
u8,
|
||||||
|
&.{ mark.basicData.templateId, mark.basicData.markInstId },
|
||||||
|
);
|
||||||
|
try map.put(arena, inst_id, mark);
|
||||||
|
};
|
||||||
|
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn deinit(assets: *Assets) void {
|
pub fn deinit(assets: *Assets) void {
|
||||||
assets.owned_tables.deinit();
|
assets.owned_tables.deinit();
|
||||||
assets.arena.deinit();
|
assets.arena.deinit();
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ pub const SkillPatchDataBundleList = @import("Tables/SkillPatchDataBundleList.zi
|
|||||||
pub const WeaponBasicData = @import("Tables/WeaponBasicData.zig");
|
pub const WeaponBasicData = @import("Tables/WeaponBasicData.zig");
|
||||||
pub const CharWpnRecommendData = @import("Tables/CharWpnRecommendData.zig");
|
pub const CharWpnRecommendData = @import("Tables/CharWpnRecommendData.zig");
|
||||||
pub const DomainData = @import("Tables/DomainData.zig");
|
pub const DomainData = @import("Tables/DomainData.zig");
|
||||||
|
pub const MapPointData = @import("Tables/MapPointData.zig");
|
||||||
|
|
||||||
pub const StrToNum = struct {
|
pub const StrToNum = struct {
|
||||||
pub const file = "StrIdNumTable.json";
|
pub const file = "StrIdNumTable.json";
|
||||||
@@ -32,6 +33,7 @@ str_to_num: StringArrayHashMap(StrToNum),
|
|||||||
num_to_str: StringArrayHashMap(NumToStr),
|
num_to_str: StringArrayHashMap(NumToStr),
|
||||||
char_wpn_recommend: StringArrayHashMap(CharWpnRecommendData),
|
char_wpn_recommend: StringArrayHashMap(CharWpnRecommendData),
|
||||||
domain_data: StringArrayHashMap(DomainData),
|
domain_data: StringArrayHashMap(DomainData),
|
||||||
|
track_map_point: StringArrayHashMap(MapPointData),
|
||||||
|
|
||||||
pub const LoadError = error{
|
pub const LoadError = error{
|
||||||
NotStarted,
|
NotStarted,
|
||||||
|
|||||||
10
gamesv/src/Assets/Tables/MapPointData.zig
Normal file
10
gamesv/src/Assets/Tables/MapPointData.zig
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
pub const file = "TrackMapPointTable.json";
|
||||||
|
|
||||||
|
start: []const u8,
|
||||||
|
end: []const u8,
|
||||||
|
quest: []const u8,
|
||||||
|
pos: struct {
|
||||||
|
x: f32,
|
||||||
|
y: f32,
|
||||||
|
z: f32,
|
||||||
|
},
|
||||||
@@ -3,6 +3,8 @@ const json = std.json;
|
|||||||
|
|
||||||
pub const CommonSkillConfig = @import("configs/CommonSkillConfig.zig");
|
pub const CommonSkillConfig = @import("configs/CommonSkillConfig.zig");
|
||||||
pub const LevelConfig = @import("configs/LevelConfig.zig");
|
pub const LevelConfig = @import("configs/LevelConfig.zig");
|
||||||
|
pub const ClientSingleMapMarkData = @import("configs/ClientSingleMapMarkData.zig");
|
||||||
|
pub const TeleportValidationDataTable = @import("configs/TeleportValidationDataTable.zig");
|
||||||
|
|
||||||
const Io = std.Io;
|
const Io = std.Io;
|
||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
|
|||||||
35
gamesv/src/Assets/configs/ClientSingleMapMarkData.zig
Normal file
35
gamesv/src/Assets/configs/ClientSingleMapMarkData.zig
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
const ClientSingleMapMarkData = @This();
|
||||||
|
|
||||||
|
basicData: MapMarkBasicData,
|
||||||
|
detailedData: ?MapMarkDetailedData = null,
|
||||||
|
|
||||||
|
pub const MapMarkBasicData = struct {
|
||||||
|
templateId: []const u8,
|
||||||
|
markInstId: []const u8,
|
||||||
|
pos: struct {
|
||||||
|
x: f32,
|
||||||
|
y: f32,
|
||||||
|
z: f32,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const MapMarkDetailedData = struct {
|
||||||
|
logicIdGlobal: ?u64 = null,
|
||||||
|
teleportValidationId: ?[]const u8 = null,
|
||||||
|
|
||||||
|
pub const TeleportValidationData = struct {
|
||||||
|
logicIdGlobal: u64,
|
||||||
|
teleportValidationId: []const u8,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn teleportValidationData(
|
||||||
|
csmmd: *const ClientSingleMapMarkData,
|
||||||
|
) ?MapMarkDetailedData.TeleportValidationData {
|
||||||
|
const details = csmmd.detailedData orelse return null;
|
||||||
|
|
||||||
|
return .{
|
||||||
|
.logicIdGlobal = details.logicIdGlobal orelse return null,
|
||||||
|
.teleportValidationId = details.teleportValidationId orelse return null,
|
||||||
|
};
|
||||||
|
}
|
||||||
10
gamesv/src/Assets/configs/TeleportValidationDataTable.zig
Normal file
10
gamesv/src/Assets/configs/TeleportValidationDataTable.zig
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
teleportValidationDatas: std.json.ArrayHashMap(TeleportValidationData),
|
||||||
|
|
||||||
|
pub const TeleportValidationData = struct {
|
||||||
|
id: []const u8,
|
||||||
|
teleportReason: i32,
|
||||||
|
sceneId: []const u8,
|
||||||
|
position: struct { x: f32, y: f32, z: f32 },
|
||||||
|
};
|
||||||
@@ -107,17 +107,20 @@ pub fn process(
|
|||||||
error.Canceled => |e| return e,
|
error.Canceled => |e| return e,
|
||||||
};
|
};
|
||||||
|
|
||||||
const player = fs.persistence.loadPlayer(io, gpa, assets, result.uid) catch |err| switch (err) {
|
const player = fs.persistence.loadPlayer(io, gpa, assets, result.uid.view()) catch |err| switch (err) {
|
||||||
error.Canceled => |e| return e,
|
error.Canceled => |e| return e,
|
||||||
else => |e| {
|
else => |e| {
|
||||||
log.err("failed to load data for player with uid {d}: {t}, disconnecting", .{ result.uid, e });
|
log.err(
|
||||||
|
"failed to load data for player with uid {s}: {t}, disconnecting",
|
||||||
|
.{ result.uid.view(), e },
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
log.info(
|
log.info(
|
||||||
"client from '{f}' has successfully logged into account with uid: {d}",
|
"client from '{f}' has successfully logged into account with uid: {s}",
|
||||||
.{ stream.socket.address, result.uid },
|
.{ stream.socket.address, result.uid.view() },
|
||||||
);
|
);
|
||||||
|
|
||||||
world = logic.World.init(&session, assets, result.uid, player, gpa, io);
|
world = logic.World.init(&session, assets, result.uid, player, gpa, io);
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const pb = @import("proto").pb;
|
const pb = @import("proto").pb;
|
||||||
|
const mem = @import("common").mem;
|
||||||
const Session = @import("../Session.zig");
|
const Session = @import("../Session.zig");
|
||||||
|
const PlayerId = @import("../logic.zig").World.PlayerId;
|
||||||
|
|
||||||
const Io = std.Io;
|
const Io = std.Io;
|
||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
@@ -10,14 +12,30 @@ const log = std.log.scoped(.auth);
|
|||||||
pub const Error = error{LoginFailed} || Session.SendError || Allocator.Error || Io.Cancelable;
|
pub const Error = error{LoginFailed} || Session.SendError || Allocator.Error || Io.Cancelable;
|
||||||
|
|
||||||
pub const Result = struct {
|
pub const Result = struct {
|
||||||
uid: u64, // It's a string in SC_LOGIN tho
|
uid: mem.LimitedString(PlayerId.max_length),
|
||||||
|
|
||||||
|
pub const FromUidSliceError = error{
|
||||||
|
TooLongString,
|
||||||
|
InvalidCharacters,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn fromUidSlice(slice: []const u8) FromUidSliceError!Result {
|
||||||
|
const result: Result = .{ .uid = try .init(slice) };
|
||||||
|
for (slice) |c| if (!std.ascii.isAlphanumeric(c)) {
|
||||||
|
return error.InvalidCharacters;
|
||||||
|
};
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn processLoginRequest(io: Io, session: *Session, request: *const pb.CS_LOGIN) Error!Result {
|
pub fn processLoginRequest(io: Io, session: *Session, request: *const pb.CS_LOGIN) Error!Result {
|
||||||
log.info("login request received: {any}", .{request});
|
log.info("login request received: {any}", .{request});
|
||||||
|
|
||||||
const uid = std.fmt.parseInt(u64, request.uid, 10) catch
|
const result = Result.fromUidSlice(request.uid) catch |err| {
|
||||||
|
log.err("invalid UID received: {t}", .{err});
|
||||||
return error.LoginFailed;
|
return error.LoginFailed;
|
||||||
|
};
|
||||||
|
|
||||||
try session.send(pb.SC_LOGIN{
|
try session.send(pb.SC_LOGIN{
|
||||||
.uid = request.uid,
|
.uid = request.uid,
|
||||||
@@ -25,5 +43,5 @@ pub fn processLoginRequest(io: Io, session: *Session, request: *const pb.CS_LOGI
|
|||||||
.server_time_zone = 3,
|
.server_time_zone = 3,
|
||||||
});
|
});
|
||||||
|
|
||||||
return .{ .uid = uid };
|
return result;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ const Assets = @import("../Assets.zig");
|
|||||||
const Io = std.Io;
|
const Io = std.Io;
|
||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
const Player = logic.Player;
|
const Player = logic.Player;
|
||||||
|
const PlayerId = logic.World.PlayerId;
|
||||||
|
|
||||||
const player_data_dir = "store/player/";
|
const player_data_dir = "store/player/";
|
||||||
const base_component_file = "base_data";
|
const base_component_file = "base_data";
|
||||||
@@ -19,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",
|
||||||
@@ -35,10 +39,12 @@ const LoadPlayerError = error{
|
|||||||
const log = std.log.scoped(.persistence);
|
const log = std.log.scoped(.persistence);
|
||||||
|
|
||||||
// Opens or creates data directory for the player with specified uid.
|
// Opens or creates data directory for the player with specified uid.
|
||||||
pub fn openPlayerDataDir(io: Io, uid: u64) !Io.Dir {
|
pub fn openPlayerDataDir(io: Io, uid: []const u8) !Io.Dir {
|
||||||
var dir_path_buf: [player_data_dir.len + 20]u8 = undefined;
|
std.debug.assert(uid.len <= PlayerId.max_length);
|
||||||
const dir_path = std.fmt.bufPrint(&dir_path_buf, player_data_dir ++ "{d}", .{uid}) catch
|
|
||||||
unreachable; // Since we're printing a u64, it shouldn't exceed the buffer.
|
var dir_path_buf: [player_data_dir.len + PlayerId.max_length]u8 = undefined;
|
||||||
|
const dir_path = std.fmt.bufPrint(&dir_path_buf, player_data_dir ++ "{s}", .{uid}) catch
|
||||||
|
unreachable;
|
||||||
|
|
||||||
const cwd: Io.Dir = .cwd();
|
const cwd: Io.Dir = .cwd();
|
||||||
return cwd.openDir(io, dir_path, .{}) catch |open_err| switch (open_err) {
|
return cwd.openDir(io, dir_path, .{}) catch |open_err| switch (open_err) {
|
||||||
@@ -53,7 +59,7 @@ pub fn openPlayerDataDir(io: Io, uid: u64) !Io.Dir {
|
|||||||
|
|
||||||
// Loads player data. Creates components that do not exist.
|
// Loads player data. Creates components that do not exist.
|
||||||
// Resets component to default if its data is corrupted.
|
// Resets component to default if its data is corrupted.
|
||||||
pub fn loadPlayer(io: Io, gpa: Allocator, assets: *const Assets, uid: u64) !Player {
|
pub fn loadPlayer(io: Io, gpa: Allocator, assets: *const Assets, uid: []const u8) !Player {
|
||||||
const data_dir = try openPlayerDataDir(io, uid);
|
const data_dir = try openPlayerDataDir(io, uid);
|
||||||
defer data_dir.close(io);
|
defer data_dir.close(io);
|
||||||
|
|
||||||
@@ -82,26 +88,31 @@ pub fn loadPlayer(io: Io, gpa: Allocator, assets: *const Assets, uid: u64) !Play
|
|||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn loadBaseComponent(
|
fn loadBaseComponent(
|
||||||
io: Io,
|
io: Io,
|
||||||
data_dir: Io.Dir,
|
data_dir: Io.Dir,
|
||||||
uid: u64,
|
uid: []const u8,
|
||||||
) !Player.Base {
|
) !Player.Base {
|
||||||
return fs.loadStruct(Player.Base, io, data_dir, base_component_file) catch |err| switch (err) {
|
return fs.loadStruct(Player.Base, io, data_dir, base_component_file) catch |err| switch (err) {
|
||||||
inline error.FileNotFound, error.ChecksumMismatch, error.ReprSizeMismatch => |e| reset: {
|
inline error.FileNotFound, error.ChecksumMismatch, error.ReprSizeMismatch => |e| reset: {
|
||||||
if (e == error.ChecksumMismatch) {
|
if (e == error.ChecksumMismatch) {
|
||||||
log.err(
|
log.err(
|
||||||
"checksum mismatched for base_data of player {d}, resetting to defaults.",
|
"checksum mismatched for base_data of player {s}, resetting to defaults.",
|
||||||
.{uid},
|
.{uid},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (e == error.ReprSizeMismatch) {
|
if (e == error.ReprSizeMismatch) {
|
||||||
log.err(
|
log.err(
|
||||||
"struct layout mismatched for base_data of player {d}, resetting to defaults.",
|
"struct layout mismatched for base_data of player {s}, resetting to defaults.",
|
||||||
.{uid},
|
.{uid},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -116,7 +127,7 @@ fn loadBaseComponent(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn loadGameVarsComponent(io: Io, gpa: Allocator, data_dir: Io.Dir, uid: u64) !Player.GameVars {
|
fn loadGameVarsComponent(io: Io, gpa: Allocator, data_dir: Io.Dir, uid: []const u8) !Player.GameVars {
|
||||||
var game_vars: Player.GameVars = undefined;
|
var game_vars: Player.GameVars = undefined;
|
||||||
|
|
||||||
game_vars.server_vars = try loadArray(
|
game_vars.server_vars = try loadArray(
|
||||||
@@ -146,7 +157,7 @@ fn loadGameVarsComponent(io: Io, gpa: Allocator, data_dir: Io.Dir, uid: u64) !Pl
|
|||||||
return game_vars;
|
return game_vars;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn loadUnlockComponent(io: Io, gpa: Allocator, data_dir: Io.Dir, uid: u64) !Player.Unlock {
|
fn loadUnlockComponent(io: Io, gpa: Allocator, data_dir: Io.Dir, uid: []const u8) !Player.Unlock {
|
||||||
var unlock: Player.Unlock = undefined;
|
var unlock: Player.Unlock = undefined;
|
||||||
|
|
||||||
unlock.unlocked_systems = try loadArray(
|
unlock.unlocked_systems = try loadArray(
|
||||||
@@ -164,7 +175,7 @@ fn loadUnlockComponent(io: Io, gpa: Allocator, data_dir: Io.Dir, uid: u64) !Play
|
|||||||
return unlock;
|
return unlock;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn loadCharBagComponent(io: Io, gpa: Allocator, data_dir: Io.Dir, uid: u64) !Player.CharBag {
|
fn loadCharBagComponent(io: Io, gpa: Allocator, data_dir: Io.Dir, uid: []const u8) !Player.CharBag {
|
||||||
const char_bag_dir = data_dir.openDir(io, char_bag_path, .{}) catch |err| switch (err) {
|
const char_bag_dir = data_dir.openDir(io, char_bag_path, .{}) catch |err| switch (err) {
|
||||||
error.FileNotFound => return error.NeedsReset,
|
error.FileNotFound => return error.NeedsReset,
|
||||||
error.Canceled => |e| return e,
|
error.Canceled => |e| return e,
|
||||||
@@ -195,14 +206,14 @@ fn loadCharBagComponent(io: Io, gpa: Allocator, data_dir: Io.Dir, uid: u64) !Pla
|
|||||||
inline error.FileNotFound, error.ChecksumMismatch, error.ReprSizeMismatch => |e| reset: {
|
inline error.FileNotFound, error.ChecksumMismatch, error.ReprSizeMismatch => |e| reset: {
|
||||||
if (e == error.ChecksumMismatch) {
|
if (e == error.ChecksumMismatch) {
|
||||||
log.err(
|
log.err(
|
||||||
"checksum mismatched for char bag metadata of player {d}, resetting to defaults.",
|
"checksum mismatched for char bag metadata of player {s}, resetting to defaults.",
|
||||||
.{uid},
|
.{uid},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (e == error.ReprSizeMismatch) {
|
if (e == error.ReprSizeMismatch) {
|
||||||
log.err(
|
log.err(
|
||||||
"struct layout mismatched for char bag metadata of player {d}, resetting to defaults.",
|
"struct layout mismatched for char bag metadata of player {s}, resetting to defaults.",
|
||||||
.{uid},
|
.{uid},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -398,19 +409,19 @@ pub fn saveCharBagComponent(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn loadBitsetComponent(io: Io, data_dir: Io.Dir, uid: u64) !Player.Bitset {
|
fn loadBitsetComponent(io: Io, data_dir: Io.Dir, uid: []const u8) !Player.Bitset {
|
||||||
return fs.loadStruct(Player.Bitset, io, data_dir, bitset_file) catch |err| switch (err) {
|
return fs.loadStruct(Player.Bitset, io, data_dir, bitset_file) catch |err| switch (err) {
|
||||||
inline error.FileNotFound, error.ChecksumMismatch, error.ReprSizeMismatch => |e| {
|
inline error.FileNotFound, error.ChecksumMismatch, error.ReprSizeMismatch => |e| {
|
||||||
if (e == error.ChecksumMismatch) {
|
if (e == error.ChecksumMismatch) {
|
||||||
log.err(
|
log.err(
|
||||||
"checksum mismatched for bitset of player {d}, resetting to defaults.",
|
"checksum mismatched for bitset of player {s}, resetting to defaults.",
|
||||||
.{uid},
|
.{uid},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (e == error.ReprSizeMismatch) {
|
if (e == error.ReprSizeMismatch) {
|
||||||
log.err(
|
log.err(
|
||||||
"struct layout mismatched for bitset of player {d}, resetting to defaults.",
|
"struct layout mismatched for bitset of player {s}, resetting to defaults.",
|
||||||
.{uid},
|
.{uid},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -443,12 +454,81 @@ 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, ¤t, 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,
|
||||||
gpa: Allocator,
|
gpa: Allocator,
|
||||||
data_dir: Io.Dir,
|
data_dir: Io.Dir,
|
||||||
uid: u64,
|
uid: []const u8,
|
||||||
sub_path: []const u8,
|
sub_path: []const u8,
|
||||||
defaults: []const T,
|
defaults: []const T,
|
||||||
) ![]T {
|
) ![]T {
|
||||||
@@ -456,14 +536,14 @@ fn loadArray(
|
|||||||
inline error.FileNotFound, error.ChecksumMismatch, error.ReprSizeMismatch => |e| reset: {
|
inline error.FileNotFound, error.ChecksumMismatch, error.ReprSizeMismatch => |e| reset: {
|
||||||
if (e == error.ChecksumMismatch) {
|
if (e == error.ChecksumMismatch) {
|
||||||
log.err(
|
log.err(
|
||||||
"checksum mismatched for '{s}' of player {d}, resetting to defaults.",
|
"checksum mismatched for '{s}' of player {s}, resetting to defaults.",
|
||||||
.{ sub_path, uid },
|
.{ sub_path, uid },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (e == error.ReprSizeMismatch) {
|
if (e == error.ReprSizeMismatch) {
|
||||||
log.err(
|
log.err(
|
||||||
"struct layout mismatched for '{s}' of player {d}, resetting to defaults.",
|
"struct layout mismatched for '{s}' of player {s}, resetting to defaults.",
|
||||||
.{ sub_path, uid },
|
.{ sub_path, uid },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
9
gamesv/src/logic/Player/Scene.zig
Normal file
9
gamesv/src/logic/Player/Scene.zig
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
const Scene = @This();
|
||||||
|
|
||||||
|
current: Current,
|
||||||
|
|
||||||
|
pub const Current = struct {
|
||||||
|
level_id: i32,
|
||||||
|
position: [3]f32,
|
||||||
|
rotation: [3]f32,
|
||||||
|
};
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
// Describes player-local state of the world.
|
// Describes player-local state of the world.
|
||||||
const World = @This();
|
const World = @This();
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
const mem = @import("common").mem;
|
||||||
const logic = @import("../logic.zig");
|
const logic = @import("../logic.zig");
|
||||||
const Session = @import("../Session.zig");
|
const Session = @import("../Session.zig");
|
||||||
const Assets = @import("../Assets.zig");
|
const Assets = @import("../Assets.zig");
|
||||||
@@ -8,7 +9,11 @@ const Assets = @import("../Assets.zig");
|
|||||||
const Io = std.Io;
|
const Io = std.Io;
|
||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
|
|
||||||
pub const PlayerId = struct { uid: u64 };
|
pub const PlayerId = struct {
|
||||||
|
pub const max_length: usize = 16;
|
||||||
|
|
||||||
|
uid: mem.LimitedString(max_length),
|
||||||
|
};
|
||||||
|
|
||||||
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?
|
||||||
@@ -18,7 +23,7 @@ player: logic.Player,
|
|||||||
pub fn init(
|
pub fn init(
|
||||||
session: *Session,
|
session: *Session,
|
||||||
assets: *const Assets,
|
assets: *const Assets,
|
||||||
uid: u64,
|
uid: mem.LimitedString(PlayerId.max_length),
|
||||||
player: logic.Player,
|
player: logic.Player,
|
||||||
gpa: Allocator,
|
gpa: Allocator,
|
||||||
io: Io,
|
io: Io,
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
pub const Login = struct {};
|
pub const Login = struct {};
|
||||||
|
|
||||||
|
pub const ChangeSceneBegin = struct {};
|
||||||
|
|
||||||
|
pub const CurrentSceneModified = struct {};
|
||||||
|
|
||||||
pub const CharBagTeamModified = struct {
|
pub const CharBagTeamModified = struct {
|
||||||
team_index: usize,
|
team_index: usize,
|
||||||
modification: enum {
|
modification: enum {
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ const namespaces = &.{
|
|||||||
@import("messaging/scene.zig"),
|
@import("messaging/scene.zig"),
|
||||||
@import("messaging/char_bag.zig"),
|
@import("messaging/char_bag.zig"),
|
||||||
@import("messaging/friend_chat.zig"),
|
@import("messaging/friend_chat.zig"),
|
||||||
|
@import("messaging/map_mark.zig"),
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn Request(comptime CSType: type) type {
|
pub fn Request(comptime CSType: type) type {
|
||||||
|
|||||||
56
gamesv/src/logic/messaging/map_mark.zig
Normal file
56
gamesv/src/logic/messaging/map_mark.zig
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
const pb = @import("proto").pb;
|
||||||
|
const logic = @import("../../logic.zig");
|
||||||
|
const Assets = @import("../../Assets.zig");
|
||||||
|
|
||||||
|
const Player = logic.Player;
|
||||||
|
const messaging = logic.messaging;
|
||||||
|
|
||||||
|
pub fn onSceneSetTrackPoint(
|
||||||
|
request: messaging.Request(pb.CS_SCENE_SET_TRACK_POINT),
|
||||||
|
assets: *const Assets,
|
||||||
|
scene: Player.Component(.scene),
|
||||||
|
change_scene_tx: logic.event.Sender(.change_scene_begin),
|
||||||
|
cur_scene_modified_tx: logic.event.Sender(.current_scene_modified),
|
||||||
|
) !void {
|
||||||
|
const log = std.log.scoped(.scene_set_track_point);
|
||||||
|
|
||||||
|
const track_point = request.message.track_point orelse return;
|
||||||
|
const point_config = assets.map_mark_table.get(track_point.inst_id) orelse {
|
||||||
|
log.debug("invalid point instance id: '{s}'", .{track_point.inst_id});
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
const teleport_validation = point_config.teleportValidationData() orelse {
|
||||||
|
// Not a teleport point.
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
const validation_config = assets.teleport_validation_table.teleportValidationDatas.map.getPtr(teleport_validation.teleportValidationId) orelse {
|
||||||
|
log.debug(
|
||||||
|
"teleport validation config '{s}' doesn't exist",
|
||||||
|
.{teleport_validation.teleportValidationId},
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
const level_config = assets.level_config_table.getPtr(validation_config.sceneId) orelse {
|
||||||
|
log.debug("level with id '{s}' doesn't exist", .{validation_config.sceneId});
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
scene.data.current.level_id = level_config.idNum;
|
||||||
|
scene.data.current.position = .{
|
||||||
|
validation_config.position.x,
|
||||||
|
validation_config.position.y,
|
||||||
|
validation_config.position.z,
|
||||||
|
};
|
||||||
|
|
||||||
|
try cur_scene_modified_tx.send(.{});
|
||||||
|
try change_scene_tx.send(.{});
|
||||||
|
|
||||||
|
log.info(
|
||||||
|
"transitioning to scene '{s}', position: {any}",
|
||||||
|
.{ validation_config.sceneId, scene.data.current.position },
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -15,12 +15,12 @@ pub fn saveCharBagTeams(
|
|||||||
player_id: logic.World.PlayerId,
|
player_id: logic.World.PlayerId,
|
||||||
io: Io,
|
io: Io,
|
||||||
) !void {
|
) !void {
|
||||||
const data_dir = fs.persistence.openPlayerDataDir(io, player_id.uid) catch |err| switch (err) {
|
const data_dir = fs.persistence.openPlayerDataDir(io, player_id.uid.view()) catch |err| switch (err) {
|
||||||
error.Canceled => |e| return e,
|
error.Canceled => |e| return e,
|
||||||
else => |e| {
|
else => |e| {
|
||||||
log.err(
|
log.err(
|
||||||
"failed to open data dir for player with uid {d}: {t}",
|
"failed to open data dir for player with uid {s}: {t}",
|
||||||
.{ player_id.uid, e },
|
.{ player_id.uid.view(), e },
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
},
|
},
|
||||||
@@ -36,3 +36,31 @@ pub fn saveCharBagTeams(
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn saveCurrentScene(
|
||||||
|
_: logic.event.Receiver(.current_scene_modified),
|
||||||
|
scene: Player.Component(.scene),
|
||||||
|
player_id: logic.World.PlayerId,
|
||||||
|
io: Io,
|
||||||
|
) !void {
|
||||||
|
const data_dir = fs.persistence.openPlayerDataDir(io, player_id.uid.view()) catch |err| switch (err) {
|
||||||
|
error.Canceled => |e| return e,
|
||||||
|
else => |e| {
|
||||||
|
log.err(
|
||||||
|
"failed to open data dir for player with uid {s}: {t}",
|
||||||
|
.{ player_id.uid.view(), e },
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
defer data_dir.close(io);
|
||||||
|
|
||||||
|
fs.persistence.saveSceneComponent(io, data_dir, scene.data) catch |err| switch (err) {
|
||||||
|
error.Canceled => |e| return e,
|
||||||
|
else => |e| {
|
||||||
|
log.err("save failed: {t}", .{e});
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
@@ -8,32 +8,37 @@ const Player = logic.Player;
|
|||||||
const ArrayList = std.ArrayList;
|
const ArrayList = std.ArrayList;
|
||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
|
|
||||||
const default_level = "map02_lv001";
|
|
||||||
|
|
||||||
pub fn enterSceneOnLogin(
|
pub fn enterSceneOnLogin(
|
||||||
rx: logic.event.Receiver(.login),
|
rx: logic.event.Receiver(.login),
|
||||||
|
tx: logic.event.Sender(.change_scene_begin),
|
||||||
|
) !void {
|
||||||
|
_ = rx;
|
||||||
|
try tx.send(.{});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn beginChangingScene(
|
||||||
|
rx: logic.event.Receiver(.change_scene_begin),
|
||||||
session: *Session,
|
session: *Session,
|
||||||
assets: *const Assets,
|
base: Player.Component(.base),
|
||||||
base_comp: Player.Component(.base),
|
scene: Player.Component(.scene),
|
||||||
) !void {
|
) !void {
|
||||||
_ = rx;
|
_ = rx;
|
||||||
|
|
||||||
const level_config = assets.level_config_table.getPtr(default_level).?;
|
|
||||||
const position: pb.VECTOR = .{
|
const position: pb.VECTOR = .{
|
||||||
.X = level_config.playerInitPos.x,
|
.X = scene.data.current.position[0],
|
||||||
.Y = level_config.playerInitPos.y,
|
.Y = scene.data.current.position[1],
|
||||||
.Z = level_config.playerInitPos.z,
|
.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 = level_config.idNum,
|
.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 = level_config.idNum,
|
.scene_num_id = scene.data.current.level_id,
|
||||||
.position = position,
|
.position = position,
|
||||||
.pass_through_data = .init,
|
.pass_through_data = .init,
|
||||||
});
|
});
|
||||||
@@ -57,7 +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),
|
||||||
|
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) {
|
||||||
@@ -65,18 +71,17 @@ pub fn syncSelfScene(
|
|||||||
.team_modified => .SLR_CHANGE_TEAM,
|
.team_modified => .SLR_CHANGE_TEAM,
|
||||||
};
|
};
|
||||||
|
|
||||||
const level_config = assets.level_config_table.getPtr(default_level).?;
|
|
||||||
const position: pb.VECTOR = .{
|
const position: pb.VECTOR = .{
|
||||||
.X = level_config.playerInitPos.x,
|
.X = scene.data.current.position[0],
|
||||||
.Y = level_config.playerInitPos.y,
|
.Y = scene.data.current.position[1],
|
||||||
.Z = level_config.playerInitPos.z,
|
.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 = level_config.idNum,
|
.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,
|
||||||
@@ -106,7 +111,7 @@ pub fn syncSelfScene(
|
|||||||
.templateid = char_template_id,
|
.templateid = char_template_id,
|
||||||
.position = position,
|
.position = position,
|
||||||
.rotation = .{},
|
.rotation = .{},
|
||||||
.scene_num_id = level_config.idNum,
|
.scene_num_id = scene.data.current.level_id,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user