First push: build.zig
First push: build.zig.zon First push: config.json First push: dispatch/build.zig First push: dispatch/build.zig.zon First push: dispatch/src/authentication.zig First push: dispatch/src/dispatch.zig First push: dispatch/src/hotfix.zig First push: dispatch/src/main.zig First push: gameserver/build.zig First push: gameserver/build.zig.zon First push: gameserver/src/Packet.zig First push: gameserver/src/Session.zig First push: gameserver/src/command.zig First push: gameserver/src/commands/help.zig First push: gameserver/src/commands/refill.zig First push: gameserver/src/commands/sync.zig First push: gameserver/src/commands/tp.zig First push: gameserver/src/commands/unstuck.zig First push: gameserver/src/commands/value.zig First push: gameserver/src/data.zig First push: gameserver/src/handlers.zig First push: gameserver/src/main.zig First push: gameserver/src/manager/battle_mgr.zig First push: gameserver/src/manager/lineup_mgr.zig First push: gameserver/src/manager/multipath_mgr.zig First push: gameserver/src/manager/scene_mgr.zig First push: gameserver/src/network.zig First push: gameserver/src/services/avatar.zig First push: gameserver/src/services/battle.zig First push: gameserver/src/services/challenge.zig First push: gameserver/src/services/chat.zig First push: gameserver/src/services/config.zig First push: gameserver/src/services/events.zig First push: gameserver/src/services/gacha.zig First push: gameserver/src/services/item.zig First push: gameserver/src/services/lineup.zig First push: gameserver/src/services/login.zig First push: gameserver/src/services/mail.zig First push: gameserver/src/services/misc copy.zig First push: gameserver/src/services/misc.zig First push: gameserver/src/services/mission.zig First push: gameserver/src/services/multipath.zig First push: gameserver/src/services/pet.zig First push: gameserver/src/services/profile.zig First push: gameserver/src/services/res_config.zig First push: gameserver/src/services/scene.zig First push: hotfix.json First push: protocol/build.zig First push: protocol/build.zig.zon First push: protocol/src/protocol.pb.zig First push: protocol/src/root.zig First push: resources/ChallengeMazeConfig.json First push: resources/MapEntrance.json First push: resources/MazePlane.json First push: resources/res.json
This commit is contained in:
parent
ab26c9ba2c
commit
78e23046f7
56 changed files with 563840 additions and 0 deletions
64
build.zig
Normal file
64
build.zig
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const protobuf = @import("protobuf");
|
||||||
|
|
||||||
|
pub fn build(b: *std.Build) void {
|
||||||
|
const target = b.standardTargetOptions(.{});
|
||||||
|
const optimize = b.standardOptimizeOption(.{});
|
||||||
|
|
||||||
|
const dep_opts = .{ .target = target, .optimize = optimize };
|
||||||
|
|
||||||
|
const protobuf_dep = b.dependency("protobuf", dep_opts);
|
||||||
|
|
||||||
|
if (std.fs.cwd().access("protocol/StarRail.proto", .{})) {
|
||||||
|
const protoc_step = protobuf.RunProtocStep.create(b, protobuf_dep.builder, target, .{
|
||||||
|
.destination_directory = b.path("protocol/src"),
|
||||||
|
.source_files = &.{
|
||||||
|
"protocol/StarRail.proto",
|
||||||
|
},
|
||||||
|
.include_directories = &.{},
|
||||||
|
});
|
||||||
|
|
||||||
|
b.getInstallStep().dependOn(&protoc_step.step);
|
||||||
|
} else |_| {} // don't invoke protoc if proto definition doesn't exist
|
||||||
|
|
||||||
|
const dispatch = b.dependency("dispatch", dep_opts);
|
||||||
|
b.installArtifact(dispatch.artifact("dispatch"));
|
||||||
|
|
||||||
|
const gameserver = b.dependency("gameserver", dep_opts);
|
||||||
|
b.installArtifact(gameserver.artifact("gameserver"));
|
||||||
|
|
||||||
|
// "run-dispatch" command
|
||||||
|
const dispatch_cmd = b.addRunArtifact(dispatch.artifact("dispatch"));
|
||||||
|
dispatch_cmd.step.dependOn(b.getInstallStep());
|
||||||
|
|
||||||
|
if (b.args) |args| {
|
||||||
|
dispatch_cmd.addArgs(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
const dispatch_step = b.step("run-dispatch", "Run the dispatch server");
|
||||||
|
dispatch_step.dependOn(&dispatch_cmd.step);
|
||||||
|
|
||||||
|
// "run-gameserver" command
|
||||||
|
const gameserver_cmd = b.addRunArtifact(gameserver.artifact("gameserver"));
|
||||||
|
gameserver_cmd.step.dependOn(b.getInstallStep());
|
||||||
|
|
||||||
|
if (b.args) |args| {
|
||||||
|
gameserver_cmd.addArgs(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
const gameserver_step = b.step("run-gameserver", "Run the game server");
|
||||||
|
gameserver_step.dependOn(&gameserver_cmd.step);
|
||||||
|
// "gen-proto"
|
||||||
|
const gen_proto = b.step("gen-proto", "generates zig files from protocol buffer definitions");
|
||||||
|
|
||||||
|
const protoc_step = protobuf.RunProtocStep.create(b, protobuf_dep.builder, target, .{
|
||||||
|
// out directory for the generated zig files
|
||||||
|
.destination_directory = b.path("protocol/src"),
|
||||||
|
.source_files = &.{
|
||||||
|
"protocol/StarRail.proto",
|
||||||
|
},
|
||||||
|
.include_directories = &.{},
|
||||||
|
});
|
||||||
|
|
||||||
|
gen_proto.dependOn(&protoc_step.step);
|
||||||
|
}
|
15
build.zig.zon
Normal file
15
build.zig.zon
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
.{
|
||||||
|
.name = "CipherSR",
|
||||||
|
.version = "0.0.1",
|
||||||
|
.minimum_zig_version = "0.13.0",
|
||||||
|
.dependencies = .{
|
||||||
|
.dispatch = .{ .path = "dispatch" },
|
||||||
|
.gameserver = .{ .path = "gameserver" },
|
||||||
|
.protocol = .{ .path = "protocol" },
|
||||||
|
.protobuf = .{
|
||||||
|
.url = "https://github.com/Arwalk/zig-protobuf/archive/7c49ed66e029c9c7e6253b3d6d256118745550a4.tar.gz",
|
||||||
|
.hash = "122063ee7ff32a3c1aefd91a46a9fc23df0571949c3a02e2f44d39afbad0b53018a3",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
.paths = .{""},
|
||||||
|
}
|
117
config.json
Normal file
117
config.json
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
{
|
||||||
|
"avatar_config": [
|
||||||
|
{
|
||||||
|
"name": "Cipher",
|
||||||
|
"id": 1406,
|
||||||
|
"hp": 100,
|
||||||
|
"sp": 50,
|
||||||
|
"level": 80,
|
||||||
|
"promotion": 6,
|
||||||
|
"rank": 6,
|
||||||
|
"lightcone": {
|
||||||
|
"id": 23043,
|
||||||
|
"rank": 5,
|
||||||
|
"level": 80,
|
||||||
|
"promotion": 6
|
||||||
|
},
|
||||||
|
"relics": [
|
||||||
|
"61261,15,1,4,8:1:1,3:4:4,7:2:2,4:2:2",
|
||||||
|
"61262,15,1,4,10:1:1,1:3:3,9:4:4,8:1:1",
|
||||||
|
"61263,15,5,4,2:3:3,4:2:2,12:2:2,6:2:2",
|
||||||
|
"61264,15,4,4,6:2:2,2:1:1,1:4:4,5:2:2",
|
||||||
|
"63145,15,9,4,9:1:1,3:2:2,7:4:4,5:2:2",
|
||||||
|
"63146,15,4,4,1:2:2,3:3:3,4:2:2,2:2:2"
|
||||||
|
],
|
||||||
|
"use_technique": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Hyacine",
|
||||||
|
"id": 1409,
|
||||||
|
"hp": 100,
|
||||||
|
"sp": 50,
|
||||||
|
"level": 80,
|
||||||
|
"promotion": 6,
|
||||||
|
"rank": 6,
|
||||||
|
"lightcone": {
|
||||||
|
"id": 23042,
|
||||||
|
"rank": 5,
|
||||||
|
"level": 80,
|
||||||
|
"promotion": 6
|
||||||
|
},
|
||||||
|
"relics": [
|
||||||
|
"61251,15,1,4,6:1:1,11:2:2,5:2:2,2:4:4",
|
||||||
|
"61252,15,1,4,5:3:3,4:1:1,7:2:2,10:3:3",
|
||||||
|
"61253,15,5,4,3:2:2,11:2:2,10:3:3,5:2:2",
|
||||||
|
"61254,15,4,4,11:3:3,9:3:3,12:1:1,4:2:2",
|
||||||
|
"63205,15,1,4,3:1:1,8:4:4,9:2:2,2:2:2",
|
||||||
|
"63206,15,2,4,6:3:3,8:3:3,11:1:1,3:2:2"
|
||||||
|
],
|
||||||
|
"use_technique": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Trailblazer",
|
||||||
|
"id": 8008,
|
||||||
|
"hp": 100,
|
||||||
|
"sp": 50,
|
||||||
|
"level": 80,
|
||||||
|
"promotion": 6,
|
||||||
|
"rank": 6,
|
||||||
|
"lightcone": {
|
||||||
|
"id": 23042,
|
||||||
|
"rank": 5,
|
||||||
|
"level": 80,
|
||||||
|
"promotion": 6
|
||||||
|
},
|
||||||
|
"relics": [
|
||||||
|
"61261,15,1,4,12:3:3,11:2:2,8:3:3,2:1:1",
|
||||||
|
"61262,15,1,4,3:3:3,8:2:2,6:2:2,1:2:2",
|
||||||
|
"61263,15,5,4,12:1:1,5:2:2,7:3:3,4:3:3",
|
||||||
|
"61264,15,4,4,6:1:1,9:3:3,2:3:3,1:2:2",
|
||||||
|
"63145,15,6,4,7:4:4,8:1:1,3:2:2,12:2:2",
|
||||||
|
"63146,15,2,4,8:2:2,6:3:3,10:3:3,1:1:1"
|
||||||
|
],
|
||||||
|
"use_technique": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Acheron",
|
||||||
|
"id": 1308,
|
||||||
|
"hp": 100,
|
||||||
|
"sp": 50,
|
||||||
|
"level": 80,
|
||||||
|
"promotion": 6,
|
||||||
|
"rank": 6,
|
||||||
|
"lightcone": {
|
||||||
|
"id": 23024,
|
||||||
|
"rank": 5,
|
||||||
|
"level": 80,
|
||||||
|
"promotion": 6
|
||||||
|
},
|
||||||
|
"relics": [
|
||||||
|
"61171,15,1,4,9:2:2,12:2:2,8:3:3,6:2:2",
|
||||||
|
"61172,15,1,4,8:4:4,4:3:3,5:1:1,6:1:1",
|
||||||
|
"61173,15,5,4,3:1:1,12:3:3,11:3:3,7:2:2",
|
||||||
|
"61174,15,4,4,5:1:1,11:2:2,4:4:4,6:2:2",
|
||||||
|
"63145,15,7,4,9:2:2,4:1:1,6:2:2,2:4:4",
|
||||||
|
"63146,15,4,4,4:2:2,7:3:3,1:2:2,2:2:2"
|
||||||
|
],
|
||||||
|
"use_technique": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"battle_config": {
|
||||||
|
"battle_id": 1,
|
||||||
|
"stage_id": 30114122,
|
||||||
|
"cycle_count": 30,
|
||||||
|
"monster_wave": [
|
||||||
|
[
|
||||||
|
4033010,
|
||||||
|
4033030,
|
||||||
|
4032030
|
||||||
|
],
|
||||||
|
[
|
||||||
|
2034010
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"monster_level": 95,
|
||||||
|
"blessings": []
|
||||||
|
}
|
||||||
|
}
|
41
dispatch/build.zig
Normal file
41
dispatch/build.zig
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
pub fn build(b: *std.Build) void {
|
||||||
|
const target = b.standardTargetOptions(.{});
|
||||||
|
const optimize = b.standardOptimizeOption(.{});
|
||||||
|
|
||||||
|
const httpz = b.dependency("httpz", .{
|
||||||
|
.optimize = optimize,
|
||||||
|
.target = target,
|
||||||
|
});
|
||||||
|
|
||||||
|
const protocol = b.dependency("protocol", .{});
|
||||||
|
|
||||||
|
const exe = b.addExecutable(.{
|
||||||
|
.name = "dispatch",
|
||||||
|
.root_source_file = b.path("src/main.zig"),
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
});
|
||||||
|
|
||||||
|
const tls12 = b.dependency("tls12", .{
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
});
|
||||||
|
|
||||||
|
exe.root_module.addImport("tls12", tls12.module("zig-tls12"));
|
||||||
|
exe.root_module.addImport("httpz", httpz.module("httpz"));
|
||||||
|
exe.root_module.addImport("protocol", protocol.module("protocol"));
|
||||||
|
|
||||||
|
b.installArtifact(exe);
|
||||||
|
|
||||||
|
const run_cmd = b.addRunArtifact(exe);
|
||||||
|
run_cmd.step.dependOn(b.getInstallStep());
|
||||||
|
|
||||||
|
if (b.args) |args| {
|
||||||
|
run_cmd.addArgs(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
const run_step = b.step("run", "Run the app");
|
||||||
|
run_step.dependOn(&run_cmd.step);
|
||||||
|
}
|
23
dispatch/build.zig.zon
Normal file
23
dispatch/build.zig.zon
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
.{
|
||||||
|
.name = "dispatch",
|
||||||
|
.version = "0.0.1",
|
||||||
|
.minimum_zig_version = "0.13.0",
|
||||||
|
.dependencies = .{
|
||||||
|
.httpz = .{
|
||||||
|
.url = "https://github.com/karlseguin/http.zig/archive/44b97984c6a9613634e238d27ca6cdff4d4abebd.tar.gz",
|
||||||
|
.hash = "122034afde1b4ff5971787929d1a4a8ef175e1c18fe0cf4ffc1af1ce0c95c7b6be7b",
|
||||||
|
},
|
||||||
|
.protocol = .{
|
||||||
|
.path = "../protocol",
|
||||||
|
},
|
||||||
|
.tls12 = .{
|
||||||
|
.url = "https://github.com/melonedo/zig-tls12/archive/f2cbb84.tar.gz",
|
||||||
|
.hash = "12202bb2c3824deecd4ccd666ca3dcb9c92c2c68698afdb92b382c0f5cb1b86da8bc",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
.paths = .{
|
||||||
|
"build.zig",
|
||||||
|
"build.zig.zon",
|
||||||
|
"src",
|
||||||
|
},
|
||||||
|
}
|
219
dispatch/src/authentication.zig
Normal file
219
dispatch/src/authentication.zig
Normal file
|
@ -0,0 +1,219 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const httpz = @import("httpz");
|
||||||
|
|
||||||
|
pub fn onShieldLogin(req: *httpz.Request, res: *httpz.Response) !void {
|
||||||
|
std.log.debug("onShieldLogin: {any}", .{req.body_len});
|
||||||
|
//std.log.debug("onShieldLogin BODY: {any}", .{req}); game breaking
|
||||||
|
|
||||||
|
try res.json(.{
|
||||||
|
.data = .{
|
||||||
|
.account = .{
|
||||||
|
.area_code = "**",
|
||||||
|
.email = "ReversedRoooms@StarRail.com",
|
||||||
|
.country = "RU",
|
||||||
|
.is_email_verify = "1",
|
||||||
|
.token = "aa",
|
||||||
|
.uid = "1337",
|
||||||
|
},
|
||||||
|
.device_grant_required = false,
|
||||||
|
.reactivate_required = false,
|
||||||
|
.realperson_required = false,
|
||||||
|
.safe_mobilerequired = false,
|
||||||
|
},
|
||||||
|
.message = "OK",
|
||||||
|
.retcode = 0,
|
||||||
|
}, .{});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn onShieldVerify(req: *httpz.Request, res: *httpz.Response) !void {
|
||||||
|
std.log.debug("onShieldVerify: {any}", .{req.body_len});
|
||||||
|
|
||||||
|
try res.json(.{
|
||||||
|
.data = .{
|
||||||
|
.account = .{
|
||||||
|
.area_code = "**",
|
||||||
|
.email = "ReversedRoooms@StarRail.com",
|
||||||
|
.country = "RU",
|
||||||
|
.is_email_verify = "1",
|
||||||
|
.token = "aa",
|
||||||
|
.uid = "1337",
|
||||||
|
},
|
||||||
|
.device_grant_required = false,
|
||||||
|
.reactivate_required = false,
|
||||||
|
.realperson_required = false,
|
||||||
|
.safe_mobilerequired = false,
|
||||||
|
},
|
||||||
|
.message = "OK",
|
||||||
|
.retcode = 0,
|
||||||
|
}, .{});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn onVerifyLogin(req: *httpz.Request, res: *httpz.Response) !void {
|
||||||
|
std.log.debug("onVerifyLogin: {any}", .{req.body_len});
|
||||||
|
|
||||||
|
var token: []const u8 = "aa";
|
||||||
|
var uid: []const u8 = "1337";
|
||||||
|
if (try req.jsonObject()) |t| {
|
||||||
|
if (t.get("token")) |token_value| {
|
||||||
|
token = token_value.string;
|
||||||
|
}
|
||||||
|
if (t.get("uid")) |uid_value| {
|
||||||
|
uid = uid_value.string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try res.json(.{
|
||||||
|
.retcode = 0,
|
||||||
|
.message = "OK",
|
||||||
|
.data = .{
|
||||||
|
.account = .{
|
||||||
|
.area_code = "**",
|
||||||
|
.country = "CN",
|
||||||
|
.is_email_verify = "1",
|
||||||
|
.email = "ReversedRoooms@StarRail.com",
|
||||||
|
.token = token,
|
||||||
|
.uid = uid,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, .{});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn onComboTokenReq(req: *httpz.Request, res: *httpz.Response) !void {
|
||||||
|
std.log.debug("onComboTokenReq: {any}", .{req.body_len});
|
||||||
|
|
||||||
|
try res.json(.{
|
||||||
|
.data = .{
|
||||||
|
.account_type = 1,
|
||||||
|
.open_id = "1337",
|
||||||
|
.combo_id = "1337",
|
||||||
|
.combo_token = "9065ad8507d5a1991cb6fddacac5999b780bbd92",
|
||||||
|
.heartbeat = false,
|
||||||
|
.data = "{\"guest\": false}",
|
||||||
|
},
|
||||||
|
.message = "OK",
|
||||||
|
.retcode = 0,
|
||||||
|
}, .{});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn onRiskyApiCheck(req: *httpz.Request, res: *httpz.Response) !void {
|
||||||
|
std.log.debug("onRiskyApiCheck: {any}", .{req.body_len});
|
||||||
|
|
||||||
|
try res.json(.{
|
||||||
|
.retcode = 0,
|
||||||
|
.message = "OK",
|
||||||
|
.data = .{
|
||||||
|
.id = "06611ed14c3131a676b19c0d34c0644b",
|
||||||
|
.action = "ACTION_NONE",
|
||||||
|
.geetest = null,
|
||||||
|
},
|
||||||
|
}, .{});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn onGetConfig(_: *httpz.Request, res: *httpz.Response) !void {
|
||||||
|
std.log.debug("onGetConfig: ", .{});
|
||||||
|
|
||||||
|
try res.json(.{
|
||||||
|
.retcode = 0,
|
||||||
|
.message = "OK",
|
||||||
|
.data = .{
|
||||||
|
.protocol = true,
|
||||||
|
.qr_enabled = false,
|
||||||
|
.log_level = "INFO",
|
||||||
|
.announce_url = "",
|
||||||
|
.push_alias_type = 0,
|
||||||
|
.disable_ysdk_guard = true,
|
||||||
|
.enable_announce_pic_popup = false,
|
||||||
|
.app_name = "崩<EFBFBD>??RPG",
|
||||||
|
.qr_enabled_apps = .{
|
||||||
|
.bbs = false,
|
||||||
|
.cloud = false,
|
||||||
|
},
|
||||||
|
.qr_app_icons = .{
|
||||||
|
.app = "",
|
||||||
|
.bbs = "",
|
||||||
|
.cloud = "",
|
||||||
|
},
|
||||||
|
.qr_cloud_display_name = "",
|
||||||
|
.enable_user_center = true,
|
||||||
|
.functional_switch_configs = .{},
|
||||||
|
},
|
||||||
|
}, .{});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn onLoadConfig(_: *httpz.Request, res: *httpz.Response) !void {
|
||||||
|
std.log.debug("onLoadConfig: ", .{});
|
||||||
|
|
||||||
|
try res.json(.{
|
||||||
|
.retcode = 0,
|
||||||
|
.message = "OK",
|
||||||
|
.data = .{
|
||||||
|
.id = 24,
|
||||||
|
.game_key = "hkrpg_global",
|
||||||
|
.client = "PC",
|
||||||
|
.identity = "I_IDENTITY",
|
||||||
|
.guest = false,
|
||||||
|
.ignore_versions = "",
|
||||||
|
.scene = "S_NORMAL",
|
||||||
|
.name = "崩<EFBFBD>??RPG",
|
||||||
|
.disable_regist = false,
|
||||||
|
.enable_email_captcha = false,
|
||||||
|
.thirdparty = .{ "fb", "tw", "gl", "ap" },
|
||||||
|
.disable_mmt = false,
|
||||||
|
.server_guest = false,
|
||||||
|
.thirdparty_ignore = .{},
|
||||||
|
.enable_ps_bind_account = false,
|
||||||
|
.thirdparty_login_configs = .{
|
||||||
|
.tw = .{
|
||||||
|
.token_type = "TK_GAME_TOKEN",
|
||||||
|
.game_token_expires_in = 2592000,
|
||||||
|
},
|
||||||
|
.ap = .{
|
||||||
|
.token_type = "TK_GAME_TOKEN",
|
||||||
|
.game_token_expires_in = 604800,
|
||||||
|
},
|
||||||
|
.fb = .{
|
||||||
|
.token_type = "TK_GAME_TOKEN",
|
||||||
|
.game_token_expires_in = 2592000,
|
||||||
|
},
|
||||||
|
.gl = .{
|
||||||
|
.token_type = "TK_GAME_TOKEN",
|
||||||
|
.game_token_expires_in = 604800,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
.initialize_firebase = false,
|
||||||
|
.bbs_auth_login = false,
|
||||||
|
.bbs_auth_login_ignore = {},
|
||||||
|
.fetch_instance_id = false,
|
||||||
|
.enable_flash_login = false,
|
||||||
|
},
|
||||||
|
}, .{});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn onappLoginByPassword(req: *httpz.Request, res: *httpz.Response) !void {
|
||||||
|
std.log.debug("onappLoginByPassword: {any}", .{req.body_len});
|
||||||
|
|
||||||
|
try res.json(.{
|
||||||
|
.retcode = 0,
|
||||||
|
.message = "OK",
|
||||||
|
.data = .{
|
||||||
|
.bind_email_action_ticket = "",
|
||||||
|
.ext_user_info = .{
|
||||||
|
.birth = "0",
|
||||||
|
.guardian_email = "",
|
||||||
|
},
|
||||||
|
.reactivate_action_token = "",
|
||||||
|
.token = .{
|
||||||
|
.token = "aa",
|
||||||
|
.token_type = "1",
|
||||||
|
},
|
||||||
|
.user_info = .{
|
||||||
|
.account_name = "ReversedRooms",
|
||||||
|
.aid = "1337",
|
||||||
|
.area_code = "**",
|
||||||
|
.country = "RU",
|
||||||
|
.email = "ReversedRoooms@StarRail.com",
|
||||||
|
.is_email_verify = "1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, .{});
|
||||||
|
}
|
213
dispatch/src/dispatch.zig
Normal file
213
dispatch/src/dispatch.zig
Normal file
|
@ -0,0 +1,213 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const httpz = @import("httpz");
|
||||||
|
const protocol = @import("protocol");
|
||||||
|
const HttpClient = @import("tls12");
|
||||||
|
const Base64Encoder = @import("std").base64.standard.Encoder;
|
||||||
|
const Base64Decoder = @import("std").base64.standard.Decoder;
|
||||||
|
const hotfixInfo = @import("hotfix.zig");
|
||||||
|
const CNPROD_HOST = "prod-gf-cn-dp01.bhsr.com";
|
||||||
|
const CNBETA_HOST = "beta-release01-cn.bhsr.com";
|
||||||
|
const OSPROD_HOST = "prod-official-asia-dp01.starrails.com";
|
||||||
|
const OSBETA_HOST = "beta-release01-asia.starrails.com";
|
||||||
|
const OSCE_HOST = "alb-ftpc0bk5jk0c9b3go4.us-east-1.alb.aliyuncs.com";
|
||||||
|
const CNCE_HOST = "alb-xvofr71l6rrvwgcupw.cn-shanghai.alb.aliyuncs.com";
|
||||||
|
|
||||||
|
pub fn onQueryDispatch(_: *httpz.Request, res: *httpz.Response) !void {
|
||||||
|
std.log.debug("onQueryDispatch", .{});
|
||||||
|
|
||||||
|
var proto = protocol.Dispatch.init(res.arena);
|
||||||
|
|
||||||
|
const region_info = protocol.RegionInfo{
|
||||||
|
.name = .{ .Const = "CipherSR" },
|
||||||
|
.display_name = .{ .Const = "CipherSR" },
|
||||||
|
.env_type = .{ .Const = "21" },
|
||||||
|
.title = .{ .Const = "CipherSR" },
|
||||||
|
.dispatch_url = .{ .Const = "http://127.0.0.1:21000/query_gateway" },
|
||||||
|
};
|
||||||
|
|
||||||
|
try proto.region_list.append(region_info);
|
||||||
|
|
||||||
|
const data = try proto.encode(res.arena);
|
||||||
|
const size = Base64Encoder.calcSize(data.len);
|
||||||
|
const output = try res.arena.alloc(u8, size);
|
||||||
|
_ = Base64Encoder.encode(output, data);
|
||||||
|
|
||||||
|
res.body = output;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn onQueryGateway(req: *httpz.Request, res: *httpz.Response) !void {
|
||||||
|
std.log.debug("onQueryGateway", .{});
|
||||||
|
|
||||||
|
var proto = protocol.Gateserver.init(res.arena);
|
||||||
|
const query = try req.query();
|
||||||
|
const version = query.get("version") orelse "";
|
||||||
|
std.log.info("Get Version >> {s}", .{version});
|
||||||
|
const dispatch_seed = query.get("dispatch_seed") orelse "";
|
||||||
|
std.log.info("Get DispatchSeed >> {s}", .{dispatch_seed});
|
||||||
|
const host = selectHost(version);
|
||||||
|
var gatewayUrl: []const u8 = undefined;
|
||||||
|
//Check if the client is CE version
|
||||||
|
if (std.mem.startsWith(u8, version, "OSCE") or std.mem.startsWith(u8, version, "CNCE")) {
|
||||||
|
gatewayUrl = constructCEUrl(host, version, dispatch_seed);
|
||||||
|
std.log.info("Constructed CE Gateway URL >> {s}", .{gatewayUrl});
|
||||||
|
} else {
|
||||||
|
gatewayUrl = constructUrl(host, version, dispatch_seed);
|
||||||
|
std.log.info("Constructed Gateway URL >> {s}", .{gatewayUrl});
|
||||||
|
}
|
||||||
|
|
||||||
|
const hotfix = try hotfixInfo.Parser(res.arena, "hotfix.json", version);
|
||||||
|
|
||||||
|
var assetBundleUrl: []const u8 = undefined;
|
||||||
|
var exResourceUrl: []const u8 = undefined;
|
||||||
|
var luaUrl: []const u8 = undefined;
|
||||||
|
var iFixUrl: []const u8 = undefined;
|
||||||
|
//var luaVersion: []const u8 = undefined;
|
||||||
|
//var iFixVersion: []const u8 = undefined;
|
||||||
|
|
||||||
|
//HTTP Request
|
||||||
|
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
|
||||||
|
defer arena.deinit();
|
||||||
|
const allocator = arena.allocator();
|
||||||
|
|
||||||
|
var client = HttpClient{ .allocator = allocator };
|
||||||
|
defer client.deinit();
|
||||||
|
try client.initDefaultProxies(allocator);
|
||||||
|
|
||||||
|
const url = gatewayUrl;
|
||||||
|
|
||||||
|
const uri = try std.Uri.parse(url);
|
||||||
|
var server_header_buffer: [1024 * 1024]u8 = undefined;
|
||||||
|
var gateway_request = try HttpClient.open(&client, .GET, uri, .{
|
||||||
|
.server_header_buffer = &server_header_buffer,
|
||||||
|
.redirect_behavior = @enumFromInt(10),
|
||||||
|
});
|
||||||
|
defer gateway_request.deinit();
|
||||||
|
|
||||||
|
try gateway_request.send();
|
||||||
|
try gateway_request.wait();
|
||||||
|
const gateway_response_body = try gateway_request.reader().readAllAlloc(allocator, 16 * 1024 * 1024);
|
||||||
|
|
||||||
|
//Base64 Decode
|
||||||
|
const decoded_len = try Base64Decoder.calcSizeForSlice(gateway_response_body);
|
||||||
|
const decoded_data = try allocator.alloc(u8, decoded_len);
|
||||||
|
defer allocator.free(decoded_data);
|
||||||
|
try Base64Decoder.decode(decoded_data, gateway_response_body);
|
||||||
|
//Gateserver Protobuf Decode
|
||||||
|
const gateserver_proto = try protocol.Gateserver.decode(decoded_data, res.arena);
|
||||||
|
|
||||||
|
//std.log.info("\x1b[33;1mEncoded Gateway Response >> {s}\x1b[0m", .{gateway_response_body});
|
||||||
|
//std.log.info("\x1b[32;1mDecoded Gateway Response >> {s}\x1b[0m", .{decoded_data});
|
||||||
|
//std.log.info("\x1b[33;1mProtobuf Message >> {}\x1b[0m", .{gateserver_proto});
|
||||||
|
|
||||||
|
assetBundleUrl = hotfix.assetBundleUrl;
|
||||||
|
//Check if the client is CE version
|
||||||
|
if (std.mem.startsWith(u8, version, "OSCE") or std.mem.startsWith(u8, version, "CNCE")) {
|
||||||
|
exResourceUrl = "";
|
||||||
|
} else {
|
||||||
|
exResourceUrl = hotfix.exResourceUrl;
|
||||||
|
}
|
||||||
|
luaUrl = hotfix.luaUrl;
|
||||||
|
iFixUrl = hotfix.iFixUrl;
|
||||||
|
|
||||||
|
if (assetBundleUrl.len == 0 or exResourceUrl.len == 0 or luaUrl.len == 0 or iFixUrl.len == 0) {
|
||||||
|
assetBundleUrl = gateserver_proto.asset_bundle_url.Owned.str;
|
||||||
|
//Check if the client is CE version
|
||||||
|
if (std.mem.startsWith(u8, version, "OSCE") or std.mem.startsWith(u8, version, "CNCE")) {
|
||||||
|
exResourceUrl = "";
|
||||||
|
} else {
|
||||||
|
exResourceUrl = gateserver_proto.ex_resource_url.Owned.str;
|
||||||
|
}
|
||||||
|
luaUrl = gateserver_proto.lua_url.Owned.str;
|
||||||
|
iFixUrl = gateserver_proto.ifix_url.Owned.str;
|
||||||
|
|
||||||
|
try hotfixInfo.putValue(version, assetBundleUrl, exResourceUrl, luaUrl, iFixUrl);
|
||||||
|
} else {
|
||||||
|
// Check if exResourceUrl has changed
|
||||||
|
const latestExResourceUrl = gateserver_proto.ex_resource_url.Owned.str;
|
||||||
|
if (!std.mem.eql(u8, exResourceUrl, latestExResourceUrl)) {
|
||||||
|
std.log.info("exResourceUrl changed from '{s}' to '{s}'", .{ exResourceUrl, latestExResourceUrl });
|
||||||
|
|
||||||
|
assetBundleUrl = gateserver_proto.asset_bundle_url.Owned.str;
|
||||||
|
//Check if the client is CE version
|
||||||
|
if (std.mem.startsWith(u8, version, "OSCE") or std.mem.startsWith(u8, version, "CNCE")) {
|
||||||
|
exResourceUrl = "";
|
||||||
|
} else {
|
||||||
|
exResourceUrl = latestExResourceUrl;
|
||||||
|
}
|
||||||
|
luaUrl = gateserver_proto.lua_url.Owned.str;
|
||||||
|
iFixUrl = gateserver_proto.ifix_url.Owned.str;
|
||||||
|
|
||||||
|
try hotfixInfo.putValue(version, assetBundleUrl, exResourceUrl, luaUrl, iFixUrl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std.log.info("Get AssetBundleUrl >> {s}", .{assetBundleUrl});
|
||||||
|
std.log.info("Get ExResourceUrl >> {s}", .{exResourceUrl});
|
||||||
|
std.log.info("Get LuaUrl >> {s}", .{luaUrl});
|
||||||
|
std.log.info("Get IFixUrl >> {s}", .{iFixUrl});
|
||||||
|
|
||||||
|
proto.retcode = 0;
|
||||||
|
proto.port = 23301;
|
||||||
|
proto.ip = .{ .Const = "127.0.0.1" };
|
||||||
|
|
||||||
|
proto.asset_bundle_url = .{ .Const = assetBundleUrl };
|
||||||
|
proto.ex_resource_url = .{ .Const = exResourceUrl };
|
||||||
|
proto.lua_url = .{ .Const = luaUrl };
|
||||||
|
|
||||||
|
proto.enable_watermark = true;
|
||||||
|
proto.network_diagnostic = true;
|
||||||
|
proto.enable_android_middle_package = true;
|
||||||
|
proto.use_new_networking = true;
|
||||||
|
|
||||||
|
//Check if the client is CE version
|
||||||
|
if (std.mem.startsWith(u8, version, "OSCE") or std.mem.startsWith(u8, version, "CNCE")) {
|
||||||
|
proto.enable_design_data_version_update = false;
|
||||||
|
proto.enable_version_update = false;
|
||||||
|
std.log.info("CE client version {s} detected, skipping design_data download.", .{version});
|
||||||
|
} else {
|
||||||
|
proto.enable_design_data_version_update = true;
|
||||||
|
proto.enable_version_update = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
proto.mtp_switch = true;
|
||||||
|
proto.forbid_recharge = true;
|
||||||
|
proto.close_redeem_code = true;
|
||||||
|
proto.ECBFEHFPOFJ = false;
|
||||||
|
proto.enable_save_replay_file = true;
|
||||||
|
proto.ios_exam = true;
|
||||||
|
proto.event_tracking_open = true;
|
||||||
|
proto.use_tcp = true;
|
||||||
|
proto.enable_upload_battle_log = false;
|
||||||
|
|
||||||
|
const data = try proto.encode(res.arena);
|
||||||
|
const size = Base64Encoder.calcSize(data.len);
|
||||||
|
const output = try res.arena.alloc(u8, size);
|
||||||
|
_ = Base64Encoder.encode(output, data);
|
||||||
|
|
||||||
|
res.body = output;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn selectHost(version: []const u8) []const u8 {
|
||||||
|
if (std.mem.startsWith(u8, version, "CNPROD")) {
|
||||||
|
return CNPROD_HOST;
|
||||||
|
} else if (std.mem.startsWith(u8, version, "CNBETA")) {
|
||||||
|
return CNBETA_HOST;
|
||||||
|
} else if (std.mem.startsWith(u8, version, "OSPROD")) {
|
||||||
|
return OSPROD_HOST;
|
||||||
|
} else if (std.mem.startsWith(u8, version, "OSBETA")) {
|
||||||
|
return OSBETA_HOST;
|
||||||
|
} else if (std.mem.startsWith(u8, version, "OSCE")) {
|
||||||
|
return OSCE_HOST;
|
||||||
|
} else if (std.mem.startsWith(u8, version, "CNCE")) {
|
||||||
|
return CNCE_HOST;
|
||||||
|
} else {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn constructUrl(host: []const u8, version: []const u8, dispatch_seed: []const u8) []const u8 {
|
||||||
|
return std.fmt.allocPrint(std.heap.page_allocator, "https://{s}/query_gateway?version={s}&dispatch_seed={s}&language_type=1&platform_type=2&channel_id=1&sub_channel_id=1&is_need_url=1&account_type=1", .{ host, version, dispatch_seed }) catch "";
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn constructCEUrl(host: []const u8, version: []const u8, dispatch_seed: []const u8) []const u8 {
|
||||||
|
return std.fmt.allocPrint(std.heap.page_allocator, "http://{s}/query_gateway?version={s}&dispatch_seed={s}&language_type=1&platform_type=2&channel_id=1&sub_channel_id=1&is_need_url=1&account_type=1", .{ host, version, dispatch_seed }) catch "";
|
||||||
|
}
|
95
dispatch/src/hotfix.zig
Normal file
95
dispatch/src/hotfix.zig
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
|
||||||
|
const hotfixInfo = struct {
|
||||||
|
clientVersion: []const u8,
|
||||||
|
assetBundleUrl: []const u8,
|
||||||
|
exResourceUrl: []const u8,
|
||||||
|
luaUrl: []const u8,
|
||||||
|
iFixUrl: []const u8,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn Parser(allocator: Allocator, filename: []const u8, version: []const u8) !hotfixInfo {
|
||||||
|
const file = try std.fs.cwd().openFile(filename, .{});
|
||||||
|
defer file.close();
|
||||||
|
|
||||||
|
const file_size = try file.getEndPos();
|
||||||
|
const buffer = try file.readToEndAlloc(allocator, file_size);
|
||||||
|
defer allocator.free(buffer);
|
||||||
|
|
||||||
|
var json_tree = try std.json.parseFromSlice(std.json.Value, allocator, buffer, .{ .ignore_unknown_fields = true });
|
||||||
|
defer json_tree.deinit();
|
||||||
|
|
||||||
|
const version_node = json_tree.value.object.get(version) orelse {
|
||||||
|
return hotfixInfo{
|
||||||
|
.clientVersion = version,
|
||||||
|
.assetBundleUrl = "",
|
||||||
|
.exResourceUrl = "",
|
||||||
|
.luaUrl = "",
|
||||||
|
.iFixUrl = "",
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
return getValue(version_node, version);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn getValue(node: std.json.Value, client_version: []const u8) !hotfixInfo {
|
||||||
|
if (node != .object) return error.InvalidJsonStructure;
|
||||||
|
|
||||||
|
const obj = node.object;
|
||||||
|
|
||||||
|
const assetBundleUrl = obj.get("asset_bundle_url") orelse return error.MissingAssetBundleUrl;
|
||||||
|
const exResourceUrl = obj.get("ex_resource_url") orelse return error.MissingExResourceUrl;
|
||||||
|
const luaUrl = obj.get("lua_url") orelse return error.MissingLuaUrl;
|
||||||
|
const iFixUrl = obj.get("ifix_url") orelse return error.MissingIFixUrl;
|
||||||
|
|
||||||
|
if (assetBundleUrl != .string or
|
||||||
|
exResourceUrl != .string or
|
||||||
|
luaUrl != .string or
|
||||||
|
iFixUrl != .string) return error.InvalidUrlFormat;
|
||||||
|
|
||||||
|
return hotfixInfo{
|
||||||
|
.clientVersion = client_version,
|
||||||
|
.assetBundleUrl = assetBundleUrl.string,
|
||||||
|
.exResourceUrl = exResourceUrl.string,
|
||||||
|
.luaUrl = luaUrl.string,
|
||||||
|
.iFixUrl = iFixUrl.string,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn putValue(version: []const u8, assetBundleUrl: []const u8, exResourceUrl: []const u8, luaUrl: []const u8, iFixUrl: []const u8) !void {
|
||||||
|
const file = try std.fs.cwd().openFile("hotfix.json", .{ .mode = .read_write });
|
||||||
|
defer file.close();
|
||||||
|
|
||||||
|
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
|
||||||
|
defer arena.deinit();
|
||||||
|
const allocator = arena.allocator();
|
||||||
|
|
||||||
|
const file_size = try file.getEndPos();
|
||||||
|
const buffer0 = try file.readToEndAlloc(allocator, file_size);
|
||||||
|
defer allocator.free(buffer0);
|
||||||
|
|
||||||
|
var json_tree = try std.json.parseFromSlice(std.json.Value, allocator, buffer0, .{ .ignore_unknown_fields = true });
|
||||||
|
defer json_tree.deinit();
|
||||||
|
|
||||||
|
var root = json_tree.value.object;
|
||||||
|
|
||||||
|
var new_version = std.json.ObjectMap.init(allocator);
|
||||||
|
try new_version.put("asset_bundle_url", .{ .string = assetBundleUrl });
|
||||||
|
try new_version.put("ex_resource_url", .{ .string = exResourceUrl });
|
||||||
|
try new_version.put("ifix_url", .{ .string = iFixUrl });
|
||||||
|
try new_version.put("ifix_version", .{ .string = "0" });
|
||||||
|
try new_version.put("lua_url", .{ .string = luaUrl });
|
||||||
|
try new_version.put("lua_version", .{ .string = "" });
|
||||||
|
try root.put(version, .{ .object = new_version });
|
||||||
|
|
||||||
|
const json_value = std.json.Value{ .object = root };
|
||||||
|
|
||||||
|
var buffer = std.ArrayList(u8).init(allocator);
|
||||||
|
try std.json.stringify(json_value, .{ .whitespace = .indent_4 }, buffer.writer());
|
||||||
|
|
||||||
|
const new_file = try std.fs.cwd().createFile("hotfix.json", .{ .truncate = true });
|
||||||
|
defer new_file.close();
|
||||||
|
|
||||||
|
try new_file.writeAll(buffer.items);
|
||||||
|
}
|
35
dispatch/src/main.zig
Normal file
35
dispatch/src/main.zig
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const builtin = @import("builtin");
|
||||||
|
const httpz = @import("httpz");
|
||||||
|
const protocol = @import("protocol");
|
||||||
|
|
||||||
|
const authentication = @import("authentication.zig");
|
||||||
|
const dispatch = @import("dispatch.zig");
|
||||||
|
const PORT = 21000;
|
||||||
|
|
||||||
|
pub const std_options = .{
|
||||||
|
.log_level = switch (builtin.mode) {
|
||||||
|
.Debug => .debug,
|
||||||
|
else => .info,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn main() !void {
|
||||||
|
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||||
|
const allocator = gpa.allocator();
|
||||||
|
|
||||||
|
var server = try httpz.Server().init(allocator, .{ .port = PORT });
|
||||||
|
defer server.stop();
|
||||||
|
defer server.deinit();
|
||||||
|
var router = server.router();
|
||||||
|
|
||||||
|
router.get("/query_dispatch", dispatch.onQueryDispatch);
|
||||||
|
router.get("/query_gateway", dispatch.onQueryGateway);
|
||||||
|
router.post("/account/risky/api/check", authentication.onRiskyApiCheck);
|
||||||
|
router.post("/:product_name/mdk/shield/api/login", authentication.onShieldLogin);
|
||||||
|
router.post("/:product_name/mdk/shield/api/verify", authentication.onVerifyLogin);
|
||||||
|
router.post("/:product_name/combo/granter/login/v2/login", authentication.onComboTokenReq);
|
||||||
|
|
||||||
|
std.log.info("Dispatch is listening at localhost:{?}", .{server.config.port});
|
||||||
|
try server.listen();
|
||||||
|
}
|
28
gameserver/build.zig
Normal file
28
gameserver/build.zig
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
pub fn build(b: *std.Build) void {
|
||||||
|
const target = b.standardTargetOptions(.{});
|
||||||
|
const optimize = b.standardOptimizeOption(.{});
|
||||||
|
|
||||||
|
const protocol = b.dependency("protocol", .{});
|
||||||
|
|
||||||
|
const exe = b.addExecutable(.{
|
||||||
|
.name = "gameserver",
|
||||||
|
.root_source_file = b.path("src/main.zig"),
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
});
|
||||||
|
|
||||||
|
exe.root_module.addImport("protocol", protocol.module("protocol"));
|
||||||
|
b.installArtifact(exe);
|
||||||
|
|
||||||
|
const run_cmd = b.addRunArtifact(exe);
|
||||||
|
run_cmd.step.dependOn(b.getInstallStep());
|
||||||
|
|
||||||
|
if (b.args) |args| {
|
||||||
|
run_cmd.addArgs(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
const run_step = b.step("run", "Run the app");
|
||||||
|
run_step.dependOn(&run_cmd.step);
|
||||||
|
}
|
18
gameserver/build.zig.zon
Normal file
18
gameserver/build.zig.zon
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
.{
|
||||||
|
.name = "gameserver",
|
||||||
|
.version = "0.0.0",
|
||||||
|
.minimum_zig_version = "0.13.0",
|
||||||
|
.dependencies = .{
|
||||||
|
.protocol = .{
|
||||||
|
.path = "../protocol",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
.paths = .{
|
||||||
|
"build.zig",
|
||||||
|
"build.zig.zon",
|
||||||
|
"src",
|
||||||
|
// For example...
|
||||||
|
//"LICENSE",
|
||||||
|
//"README.md",
|
||||||
|
},
|
||||||
|
}
|
77
gameserver/src/Packet.zig
Normal file
77
gameserver/src/Packet.zig
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
const Reader = std.net.Stream.Reader;
|
||||||
|
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
cmd_id: u16,
|
||||||
|
head: []u8,
|
||||||
|
body: []u8,
|
||||||
|
allocator: Allocator,
|
||||||
|
|
||||||
|
const head_magic: u32 = 0x9D74C714;
|
||||||
|
const tail_magic: u32 = 0xD7A152C8;
|
||||||
|
|
||||||
|
pub const DecodeError = error{
|
||||||
|
HeadMagicMismatch,
|
||||||
|
TailMagicMismatch,
|
||||||
|
PayloadTooBig,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn read(reader: *Reader, allocator: Allocator) !Self {
|
||||||
|
if (try reader.readInt(u32, .big) != Self.head_magic) {
|
||||||
|
return Self.DecodeError.HeadMagicMismatch;
|
||||||
|
}
|
||||||
|
|
||||||
|
const cmd_id = try reader.readInt(u16, .big);
|
||||||
|
const head_len = try reader.readInt(u16, .big);
|
||||||
|
const body_len = try reader.readInt(u32, .big);
|
||||||
|
|
||||||
|
if (body_len > 0xFFFFFF) {
|
||||||
|
return Self.DecodeError.PayloadTooBig;
|
||||||
|
}
|
||||||
|
|
||||||
|
const head = try allocator.alloc(u8, head_len);
|
||||||
|
errdefer allocator.free(head);
|
||||||
|
|
||||||
|
const body = try allocator.alloc(u8, body_len);
|
||||||
|
errdefer allocator.free(body);
|
||||||
|
|
||||||
|
_ = try reader.readAll(head);
|
||||||
|
_ = try reader.readAll(body);
|
||||||
|
|
||||||
|
if (try reader.readInt(u32, .big) != Self.tail_magic) {
|
||||||
|
return Self.DecodeError.TailMagicMismatch;
|
||||||
|
}
|
||||||
|
|
||||||
|
return .{
|
||||||
|
.cmd_id = cmd_id,
|
||||||
|
.head = head,
|
||||||
|
.body = body,
|
||||||
|
.allocator = allocator,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getProto(self: *const Self, comptime T: type, allocator: Allocator) !T {
|
||||||
|
return try T.decode(self.body, allocator);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn encode(cmd_id: u16, head: []u8, body: []u8, allocator: Allocator) ![]u8 {
|
||||||
|
var buf = try allocator.alloc(u8, 16 + head.len + body.len);
|
||||||
|
|
||||||
|
std.mem.writeInt(u32, buf[0..4], Self.head_magic, .big);
|
||||||
|
std.mem.writeInt(u16, buf[4..6], cmd_id, .big);
|
||||||
|
std.mem.writeInt(u16, buf[6..8], @intCast(head.len), .big);
|
||||||
|
std.mem.writeInt(u32, buf[8..12], @intCast(body.len), .big);
|
||||||
|
@memcpy(buf[12..(12 + head.len)], head);
|
||||||
|
@memcpy(buf[(12 + head.len)..(12 + head.len + body.len)], body);
|
||||||
|
std.mem.writeInt(u32, buf[(12 + head.len + body.len)..][0..4], Self.tail_magic, .big);
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deinit(self: *Self) void {
|
||||||
|
self.allocator.free(self.head);
|
||||||
|
self.allocator.free(self.body);
|
||||||
|
}
|
55
gameserver/src/Session.zig
Normal file
55
gameserver/src/Session.zig
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const protocol = @import("protocol");
|
||||||
|
const handlers = @import("handlers.zig");
|
||||||
|
const Packet = @import("Packet.zig");
|
||||||
|
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
const ArenaAllocator = std.heap.ArenaAllocator;
|
||||||
|
|
||||||
|
const Stream = std.net.Stream;
|
||||||
|
const Address = std.net.Address;
|
||||||
|
|
||||||
|
const Self = @This();
|
||||||
|
const log = std.log.scoped(.session);
|
||||||
|
|
||||||
|
address: Address,
|
||||||
|
stream: Stream,
|
||||||
|
allocator: Allocator,
|
||||||
|
|
||||||
|
pub fn init(address: Address, stream: Stream, allocator: Allocator) Self {
|
||||||
|
return .{
|
||||||
|
.address = address,
|
||||||
|
.stream = stream,
|
||||||
|
.allocator = allocator,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run(self: *Self) !void {
|
||||||
|
defer self.stream.close();
|
||||||
|
|
||||||
|
var reader = self.stream.reader();
|
||||||
|
while (true) {
|
||||||
|
var packet = Packet.read(&reader, self.allocator) catch break;
|
||||||
|
defer packet.deinit();
|
||||||
|
|
||||||
|
try handlers.handle(self, &packet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send(self: *Self, cmd_id: protocol.CmdID, proto: anytype) !void {
|
||||||
|
const data = try proto.encode(self.allocator);
|
||||||
|
defer self.allocator.free(data);
|
||||||
|
|
||||||
|
const packet = try Packet.encode(@intFromEnum(cmd_id), &.{}, data, self.allocator);
|
||||||
|
defer self.allocator.free(packet);
|
||||||
|
|
||||||
|
_ = try self.stream.write(packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send_empty(self: *Self, cmd_id: protocol.CmdID) !void {
|
||||||
|
const packet = try Packet.encode(@intFromEnum(cmd_id), &.{}, &.{}, self.allocator);
|
||||||
|
defer self.allocator.free(packet);
|
||||||
|
|
||||||
|
_ = try self.stream.write(packet);
|
||||||
|
log.debug("sent EMPTY packet with id {}", .{cmd_id});
|
||||||
|
}
|
71
gameserver/src/command.zig
Normal file
71
gameserver/src/command.zig
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const protocol = @import("protocol");
|
||||||
|
const Session = @import("Session.zig");
|
||||||
|
const Packet = @import("Packet.zig");
|
||||||
|
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
const ArenaAllocator = std.heap.ArenaAllocator;
|
||||||
|
const CmdID = protocol.CmdID;
|
||||||
|
|
||||||
|
const value_command = @import("./commands/value.zig");
|
||||||
|
const help_command = @import("./commands/help.zig");
|
||||||
|
const tp_command = @import("./commands/tp.zig");
|
||||||
|
const unstuck_command = @import("./commands/unstuck.zig");
|
||||||
|
const sync_command = @import("./commands/sync.zig");
|
||||||
|
const refill_command = @import("./commands/refill.zig");
|
||||||
|
|
||||||
|
// Add other errors if needed
|
||||||
|
const SystemErrors = error{ CommandError, SystemResources, Unexpected, AccessDenied, WouldBlock, ConnectionResetByPeer, OutOfMemory, DiskQuota, FileTooBig, InputOutput, NoSpaceLeft, DeviceBusy, InvalidArgument, BrokenPipe, OperationAborted };
|
||||||
|
const FileErrors = error{ NotOpenForWriting, LockViolation, Overflow, InvalidCharacter, ProcessFdQuotaExceeded, SystemFdQuotaExceeded, SymLinkLoop, NameTooLong, FileNotFound, NotDir, NoDevice, SharingViolation, PathAlreadyExists, PipeBusy, InvalidUtf8, InvalidWtf8, BadPathName, NetworkNotFound, AntivirusInterference, IsDir, FileLocksNotSupported, FileBusy };
|
||||||
|
const NetworkErrors = error{ ConnectionTimedOut, NotOpenForReading, SocketNotConnected, Unseekable, StreamTooLong };
|
||||||
|
const ParseErrors = error{ UnexpectedToken, InvalidNumber, InvalidEnumTag, DuplicateField, UnknownField, MissingField, LengthMismatch, SyntaxError, UnexpectedEndOfInput, BufferUnderrun, ValueTooLong, InsufficientTokens, InvalidFormat };
|
||||||
|
const MiscErrors = error{ PermissionDenied, NetworkSubsystemFailed, FileSystem, CurrentWorkingDirectoryUnlinked, InvalidBatchScriptArg, InvalidExe, ResourceLimitReached, InvalidUserId, InvalidName, InvalidHandle, WaitAbandoned, WaitTimeOut, StdoutStreamTooLong, StderrStreamTooLong };
|
||||||
|
pub const Error = SystemErrors || FileErrors || NetworkErrors || ParseErrors || MiscErrors;
|
||||||
|
|
||||||
|
const CommandFn = *const fn (session: *Session, args: []const u8, allocator: Allocator) Error!void;
|
||||||
|
|
||||||
|
const Command = struct {
|
||||||
|
name: []const u8,
|
||||||
|
action: []const u8,
|
||||||
|
func: CommandFn,
|
||||||
|
};
|
||||||
|
|
||||||
|
const commandList = [_]Command{
|
||||||
|
Command{ .name = "help", .action = "", .func = help_command.handle },
|
||||||
|
Command{ .name = "test", .action = "", .func = value_command.handle },
|
||||||
|
Command{ .name = "node", .action = "", .func = value_command.challengeNode },
|
||||||
|
Command{ .name = "set", .action = "", .func = value_command.setGachaCommand },
|
||||||
|
Command{ .name = "tp", .action = "", .func = tp_command.handle },
|
||||||
|
Command{ .name = "unstuck", .action = "", .func = unstuck_command.handle },
|
||||||
|
Command{ .name = "sync", .action = "", .func = sync_command.onGenerateAndSync },
|
||||||
|
Command{ .name = "refill", .action = "", .func = refill_command.onRefill },
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn handleCommand(session: *Session, msg: []const u8, allocator: Allocator) Error!void {
|
||||||
|
if (msg.len < 1 or msg[0] != '/') {
|
||||||
|
std.debug.print("Message Text 2: {any}\n", .{msg});
|
||||||
|
return sendMessage(session, "Commands must start with a '/'", allocator);
|
||||||
|
}
|
||||||
|
|
||||||
|
const input = msg[1..]; // Remove the leading '/'
|
||||||
|
var tokenizer = std.mem.tokenize(u8, input, " ");
|
||||||
|
const command = tokenizer.next().?;
|
||||||
|
const args = tokenizer.rest();
|
||||||
|
|
||||||
|
for (commandList) |cmd| {
|
||||||
|
if (std.mem.eql(u8, cmd.name, command)) {
|
||||||
|
return try cmd.func(session, args, allocator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try sendMessage(session, "Invalid command", allocator);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sendMessage(session: *Session, msg: []const u8, allocator: Allocator) Error!void {
|
||||||
|
var chat = protocol.RevcMsgScNotify.init(allocator);
|
||||||
|
chat.message_type = protocol.MsgType.MSG_TYPE_CUSTOM_TEXT;
|
||||||
|
chat.chat_type = protocol.ChatType.CHAT_TYPE_PRIVATE;
|
||||||
|
chat.source_uid = 2000;
|
||||||
|
chat.message_text = .{ .Const = msg };
|
||||||
|
chat.target_uid = 1; // receiver_id
|
||||||
|
try session.send(CmdID.CmdRevcMsgScNotify, chat);
|
||||||
|
}
|
15
gameserver/src/commands/help.zig
Normal file
15
gameserver/src/commands/help.zig
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
const commandhandler = @import("../command.zig");
|
||||||
|
const std = @import("std");
|
||||||
|
const Session = @import("../Session.zig");
|
||||||
|
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
const Error = commandhandler.Error;
|
||||||
|
|
||||||
|
pub fn handle(session: *Session, _: []const u8, allocator: Allocator) Error!void {
|
||||||
|
try commandhandler.sendMessage(session, "/tp to teleport, /sync to sync data from config\n", allocator);
|
||||||
|
try commandhandler.sendMessage(session, "/refill to refill technique point after battle\n", allocator);
|
||||||
|
try commandhandler.sendMessage(session, "/set to set gacha banner\n", allocator);
|
||||||
|
try commandhandler.sendMessage(session, "/node to chage node in PF, AS, MoC\n", allocator);
|
||||||
|
try commandhandler.sendMessage(session, "You can enter MoC, PF, AS via F4 menu\n", allocator);
|
||||||
|
try commandhandler.sendMessage(session, "(If your Castorice technique enabled, you must enter battle by using Castorice's technique)\n", allocator);
|
||||||
|
}
|
22
gameserver/src/commands/refill.zig
Normal file
22
gameserver/src/commands/refill.zig
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
const commandhandler = @import("../command.zig");
|
||||||
|
const std = @import("std");
|
||||||
|
const Session = @import("../Session.zig");
|
||||||
|
const protocol = @import("protocol");
|
||||||
|
const Packet = @import("../Packet.zig");
|
||||||
|
const Config = @import("../services/config.zig");
|
||||||
|
const Data = @import("../data.zig");
|
||||||
|
const LineupManager = @import("../manager/lineup_mgr.zig").LineupManager;
|
||||||
|
|
||||||
|
const ArrayList = std.ArrayList;
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
const CmdID = protocol.CmdID;
|
||||||
|
const Error = commandhandler.Error;
|
||||||
|
|
||||||
|
pub fn onRefill(session: *Session, _: []const u8, allocator: Allocator) Error!void {
|
||||||
|
try commandhandler.sendMessage(session, "Refill skill point\n", allocator);
|
||||||
|
var sync = protocol.SyncLineupNotify.init(allocator);
|
||||||
|
var lineup_mgr = LineupManager.init(allocator);
|
||||||
|
const lineup = try lineup_mgr.createLineup();
|
||||||
|
sync.Lineup = lineup;
|
||||||
|
try session.send(CmdID.CmdSyncLineupNotify, sync);
|
||||||
|
}
|
271
gameserver/src/commands/sync.zig
Normal file
271
gameserver/src/commands/sync.zig
Normal file
|
@ -0,0 +1,271 @@
|
||||||
|
const commandhandler = @import("../command.zig");
|
||||||
|
const std = @import("std");
|
||||||
|
const Session = @import("../Session.zig");
|
||||||
|
const protocol = @import("protocol");
|
||||||
|
const Packet = @import("../Packet.zig");
|
||||||
|
const Config = @import("../services/config.zig");
|
||||||
|
const Data = @import("../data.zig");
|
||||||
|
|
||||||
|
const ArrayList = std.ArrayList;
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
const CmdID = protocol.CmdID;
|
||||||
|
const Error = commandhandler.Error;
|
||||||
|
|
||||||
|
// function to check the list if true
|
||||||
|
fn isInList(id: u32, list: []const u32) bool {
|
||||||
|
for (list) |item| {
|
||||||
|
if (item == id) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn syncItems(session: *Session, allocator: Allocator, equip_avatar: bool) !void {
|
||||||
|
var sync = protocol.PlayerSyncScNotify.init(allocator);
|
||||||
|
const config = try Config.loadGameConfig(allocator, "config.json");
|
||||||
|
for (config.avatar_config.items) |avatarConf| {
|
||||||
|
const equip_avatar_id: u32 = if (equip_avatar) avatarConf.id else 0;
|
||||||
|
const lc = protocol.Equipment{
|
||||||
|
.unique_id = if (equip_avatar) nextGlobalId(.Default) else nextGlobalId(.Undress),
|
||||||
|
.tid = avatarConf.lightcone.id,
|
||||||
|
.is_protected = true,
|
||||||
|
.level = avatarConf.lightcone.level,
|
||||||
|
.rank = avatarConf.lightcone.rank,
|
||||||
|
.promotion = avatarConf.lightcone.promotion,
|
||||||
|
.equip_avatar_id = equip_avatar_id,
|
||||||
|
};
|
||||||
|
try sync.equipment_list.append(lc);
|
||||||
|
for (avatarConf.relics.items) |input| {
|
||||||
|
var r = protocol.Relic{
|
||||||
|
.tid = input.id,
|
||||||
|
.main_affix_id = input.main_affix_id,
|
||||||
|
.unique_id = if (equip_avatar) nextGlobalId(.Default) else nextGlobalId(.Undress),
|
||||||
|
.exp = 0,
|
||||||
|
.equip_avatar_id = equip_avatar_id,
|
||||||
|
.is_protected = true,
|
||||||
|
.level = input.level,
|
||||||
|
.sub_affix_list = ArrayList(protocol.RelicAffix).init(allocator),
|
||||||
|
.reforge_sub_affix_list = ArrayList(protocol.RelicAffix).init(allocator),
|
||||||
|
};
|
||||||
|
try r.sub_affix_list.append(protocol.RelicAffix{ .affix_id = input.stat1, .cnt = input.cnt1, .step = input.step1 });
|
||||||
|
try r.sub_affix_list.append(protocol.RelicAffix{ .affix_id = input.stat2, .cnt = input.cnt2, .step = input.step2 });
|
||||||
|
try r.sub_affix_list.append(protocol.RelicAffix{ .affix_id = input.stat3, .cnt = input.cnt3, .step = input.step3 });
|
||||||
|
try r.sub_affix_list.append(protocol.RelicAffix{ .affix_id = input.stat4, .cnt = input.cnt4, .step = input.step4 });
|
||||||
|
try sync.relic_list.append(r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try session.send(CmdID.CmdPlayerSyncScNotify, sync);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn onSyncEquipment(session: *Session, _: []const u8, allocator: Allocator) Error!void {
|
||||||
|
try syncItems(session, allocator, false);
|
||||||
|
try syncItems(session, allocator, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn onSyncAvatar(session: *Session, _: []const u8, allocator: Allocator) Error!void {
|
||||||
|
var sync = protocol.PlayerSyncScNotify.init(allocator);
|
||||||
|
const config = try Config.loadGameConfig(allocator, "config.json");
|
||||||
|
var char = protocol.AvatarSync.init(allocator);
|
||||||
|
for (Data.AllAvatars) |id| {
|
||||||
|
var avatar = protocol.Avatar.init(allocator);
|
||||||
|
avatar.base_avatar_id = id;
|
||||||
|
avatar.level = 80;
|
||||||
|
avatar.promotion = 6;
|
||||||
|
avatar.rank = 6;
|
||||||
|
avatar.has_taken_promotion_reward_list = ArrayList(u32).init(allocator);
|
||||||
|
for (1..6) |i| {
|
||||||
|
try avatar.has_taken_promotion_reward_list.append(@intCast(i));
|
||||||
|
}
|
||||||
|
var talentLevel: u32 = 0;
|
||||||
|
const skill_list: []const u32 = if (isInList(avatar.base_avatar_id, &Data.Rem)) &Data.skills else &Data.skills_old;
|
||||||
|
for (skill_list) |elem| {
|
||||||
|
talentLevel = switch (elem) {
|
||||||
|
1 => 6,
|
||||||
|
2...4 => 10,
|
||||||
|
301, 302 => if (isInList(avatar.base_avatar_id, &Data.Rem)) 6 else 1,
|
||||||
|
else => 1,
|
||||||
|
};
|
||||||
|
const talent = protocol.AvatarSkillTree{ .point_id = avatar.base_avatar_id * 1000 + elem, .level = talentLevel };
|
||||||
|
try avatar.skilltree_list.append(talent);
|
||||||
|
}
|
||||||
|
try char.avatar_list.append(avatar);
|
||||||
|
}
|
||||||
|
// rewrite data of avatar in config
|
||||||
|
for (config.avatar_config.items) |avatarConf| {
|
||||||
|
var avatar = protocol.Avatar.init(allocator);
|
||||||
|
// basic info
|
||||||
|
avatar.base_avatar_id = switch (avatarConf.id) {
|
||||||
|
8001...8008 => 8001,
|
||||||
|
1224 => 1001,
|
||||||
|
else => avatarConf.id,
|
||||||
|
};
|
||||||
|
avatar.level = avatarConf.level;
|
||||||
|
avatar.promotion = avatarConf.promotion;
|
||||||
|
avatar.rank = avatarConf.rank;
|
||||||
|
avatar.equipment_unique_id = nextGlobalId(.Avatar);
|
||||||
|
std.debug.print("LIGHTCONE SYNC {}\n", .{avatar.equipment_unique_id});
|
||||||
|
avatar.equip_relic_list = ArrayList(protocol.EquipRelic).init(allocator);
|
||||||
|
for (0..6) |i| {
|
||||||
|
try avatar.equip_relic_list.append(.{
|
||||||
|
.relic_unique_id = nextGlobalId(.Avatar), // uid
|
||||||
|
.type = @intCast(i), // slot
|
||||||
|
});
|
||||||
|
std.debug.print("EQUIPING SYNC {}:{}:{}\n", .{ avatarConf.id, avatar.equip_relic_list.items[i].relic_unique_id, i });
|
||||||
|
}
|
||||||
|
var talentLevel: u32 = 0;
|
||||||
|
const skill_list: []const u32 = if (isInList(avatar.base_avatar_id, &Data.Rem)) &Data.skills else &Data.skills_old;
|
||||||
|
for (skill_list) |elem| {
|
||||||
|
talentLevel = switch (elem) {
|
||||||
|
1 => 6,
|
||||||
|
2...4 => 10,
|
||||||
|
301, 302 => if (isInList(avatar.base_avatar_id, &Data.Rem)) 6 else 1,
|
||||||
|
else => 1,
|
||||||
|
};
|
||||||
|
const talent = protocol.AvatarSkillTree{ .point_id = avatar.base_avatar_id * 1000 + elem, .level = talentLevel };
|
||||||
|
try avatar.skilltree_list.append(talent);
|
||||||
|
}
|
||||||
|
try char.avatar_list.append(avatar);
|
||||||
|
const avatarType: protocol.MultiPathAvatarType = @enumFromInt(avatarConf.id);
|
||||||
|
if (@intFromEnum(avatarType) > 1) {
|
||||||
|
std.debug.print("setting avatar type: {}\n", .{avatarConf.id});
|
||||||
|
try session.send(CmdID.CmdSetAvatarPathScRsp, protocol.SetAvatarPathScRsp{
|
||||||
|
.retcode = 0,
|
||||||
|
.avatar_id = avatarType,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sync.avatar_sync = char;
|
||||||
|
try session.send(CmdID.CmdPlayerSyncScNotify, sync);
|
||||||
|
}
|
||||||
|
// TODO: DO WITH MALE MC TOO :Đ
|
||||||
|
pub fn onSyncMultiPath(session: *Session, _: []const u8, allocator: Allocator) Error!void {
|
||||||
|
var sync = protocol.PlayerSyncScNotify.init(allocator);
|
||||||
|
const config = try Config.loadGameConfig(allocator, "config.json");
|
||||||
|
const currentAvatarId = getCurrentGlobalId(.Avatar);
|
||||||
|
const GeneratorType = UidGen();
|
||||||
|
const avatar_ids = [_][]const u32{
|
||||||
|
&[_]u32{ 8001, 8002 },
|
||||||
|
&[_]u32{ 8003, 8004 },
|
||||||
|
&[_]u32{ 8005, 8006 },
|
||||||
|
&[_]u32{ 8007, 8008 },
|
||||||
|
&[_]u32{1001},
|
||||||
|
&[_]u32{1224},
|
||||||
|
};
|
||||||
|
const avatar_types = [_]protocol.MultiPathAvatarType{
|
||||||
|
.GirlWarriorType, .GirlKnightType, .GirlShamanType,
|
||||||
|
.GirlMemoryType, .Mar_7thKnightType, .Mar_7thRogueType,
|
||||||
|
};
|
||||||
|
var indexes: [6]u32 = [_]u32{0} ** 6;
|
||||||
|
var counts: [6]u32 = [_]u32{0} ** 6;
|
||||||
|
var multis: [6]protocol.MultiPathAvatarInfo = undefined;
|
||||||
|
for (&multis, avatar_types, 0..) |*multi, avatar_type, i| {
|
||||||
|
std.debug.print("MULTIPATH AVATAR INDEX: {} IS {}\n", .{ i, avatar_type });
|
||||||
|
multi.* = protocol.MultiPathAvatarInfo.init(allocator);
|
||||||
|
multi.avatar_id = avatar_type;
|
||||||
|
if (avatar_type == .Mar_7thKnightType) {
|
||||||
|
multi.dressed_skin_id = 1100101;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (config.avatar_config.items) |avatar| {
|
||||||
|
for (0..avatar_ids.len) |i| {
|
||||||
|
counts[i] += 1;
|
||||||
|
for (avatar_ids[i]) |id| {
|
||||||
|
if (avatar.id == id) {
|
||||||
|
multis[i].rank = avatar.rank;
|
||||||
|
indexes[i] = 5 - counts[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var generators: [6]GeneratorType = undefined;
|
||||||
|
for (0..multis.len) |i| {
|
||||||
|
generators[i] = GeneratorType.init(currentAvatarId - (indexes[i] * 7) + 1);
|
||||||
|
}
|
||||||
|
for (0..multis.len) |i| {
|
||||||
|
var multi = &multis[i];
|
||||||
|
var gen = &generators[i];
|
||||||
|
|
||||||
|
multi.path_equipment_id = currentAvatarId - (indexes[i] * 7) + 1;
|
||||||
|
multi.equip_relic_list = ArrayList(protocol.EquipRelic).init(allocator);
|
||||||
|
|
||||||
|
for (0..6) |slot| {
|
||||||
|
try multi.equip_relic_list.append(.{
|
||||||
|
.relic_unique_id = gen.nextId(),
|
||||||
|
.type = @intCast(slot),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (0..multis.len) |i| {
|
||||||
|
const skill_set = if (i == 3) &Data.skills else &Data.skills_old;
|
||||||
|
for (skill_set) |skill| {
|
||||||
|
const talent_level: u32 = if (skill == 1 or skill == 301 or skill == 302) 6 else if (skill <= 4) 10 else 1;
|
||||||
|
const point_id = if (avatar_ids[i].len > 1)
|
||||||
|
avatar_ids[i][1] * 1000 + skill
|
||||||
|
else
|
||||||
|
avatar_ids[i][0] * 1000 + skill;
|
||||||
|
const talent = protocol.AvatarSkillTree{
|
||||||
|
.point_id = point_id,
|
||||||
|
.level = talent_level,
|
||||||
|
};
|
||||||
|
try multis[i].multi_path_skill_tree.append(talent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try sync.multi_path_avatar_info_list.appendSlice(&multis);
|
||||||
|
try session.send(CmdID.CmdPlayerSyncScNotify, sync);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const UidType = enum { Default, Undress, Avatar };
|
||||||
|
pub var global_uid_gen = UidGenerator.init(.Default, 28);
|
||||||
|
pub var global_uid_gen_undress = UidGenerator.init(.Undress, 0);
|
||||||
|
pub var global_uid_gen_avatar = UidGenerator.init(.Avatar, 28);
|
||||||
|
pub fn nextGlobalId(uid_type: UidType) u32 {
|
||||||
|
return switch (uid_type) {
|
||||||
|
.Default => global_uid_gen.nextId(),
|
||||||
|
.Undress => global_uid_gen_undress.nextId(),
|
||||||
|
.Avatar => global_uid_gen_avatar.nextId(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
pub fn getCurrentGlobalId(uid_type: UidType) u32 {
|
||||||
|
return switch (uid_type) {
|
||||||
|
.Default => global_uid_gen.getCurrentId(),
|
||||||
|
.Undress => global_uid_gen_undress.getCurrentId(),
|
||||||
|
.Avatar => global_uid_gen_avatar.getCurrentId(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
pub const UidGenerator = struct {
|
||||||
|
current_id: u32,
|
||||||
|
uid_type: UidType,
|
||||||
|
|
||||||
|
pub fn init(uid_type: UidType, start_id: u32) UidGenerator {
|
||||||
|
return UidGenerator{ .uid_type = uid_type, .current_id = start_id };
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn nextId(self: *UidGenerator) u32 {
|
||||||
|
self.current_id += 1;
|
||||||
|
return self.current_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getCurrentId(self: *const UidGenerator) u32 {
|
||||||
|
return self.current_id;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
pub fn UidGen() type {
|
||||||
|
return struct {
|
||||||
|
current_id: u32,
|
||||||
|
const Self = @This();
|
||||||
|
pub fn init(initial_id: u32) Self {
|
||||||
|
return Self{ .current_id = initial_id };
|
||||||
|
}
|
||||||
|
pub fn nextId(self: *Self) u32 {
|
||||||
|
self.current_id +%= 1;
|
||||||
|
return self.current_id;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
pub fn onGenerateAndSync(session: *Session, placeholder: []const u8, allocator: Allocator) Error!void {
|
||||||
|
try commandhandler.sendMessage(session, "Sync items with config\n", allocator);
|
||||||
|
try onSyncEquipment(session, placeholder, allocator);
|
||||||
|
try onSyncAvatar(session, placeholder, allocator);
|
||||||
|
try onSyncMultiPath(session, placeholder, allocator);
|
||||||
|
}
|
61
gameserver/src/commands/tp.zig
Normal file
61
gameserver/src/commands/tp.zig
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
const commandhandler = @import("../command.zig");
|
||||||
|
const std = @import("std");
|
||||||
|
const Session = @import("../Session.zig");
|
||||||
|
const protocol = @import("protocol");
|
||||||
|
const Config = @import("../services/config.zig");
|
||||||
|
const Res_config = @import("../services/res_config.zig");
|
||||||
|
const LineupManager = @import("../manager/lineup_mgr.zig").LineupManager;
|
||||||
|
const SceneManager = @import("../manager/scene_mgr.zig").SceneManager;
|
||||||
|
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
const CmdID = protocol.CmdID;
|
||||||
|
const Error = commandhandler.Error;
|
||||||
|
|
||||||
|
pub fn handle(session: *Session, args: []const u8, allocator: Allocator) Error!void {
|
||||||
|
var arg_iter = std.mem.split(u8, args, " ");
|
||||||
|
const entry_id_str = arg_iter.next() orelse {
|
||||||
|
try commandhandler.sendMessage(session, "Error: Missing arguments.\nUsage: /tp <entry_id> [plane_id] [floor_id]", allocator);
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
const entry_id = std.fmt.parseInt(u32, entry_id_str, 10) catch {
|
||||||
|
try commandhandler.sendMessage(session, "Error: Invalid entry ID. Please provide a valid unsigned 32-bit integer.", allocator);
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
var plane_id: ?u32 = null;
|
||||||
|
if (arg_iter.next()) |plane_id_str| {
|
||||||
|
plane_id = std.fmt.parseInt(u32, plane_id_str, 10) catch {
|
||||||
|
try commandhandler.sendMessage(session, "Error: Invalid plane ID. Please provide a valid unsigned 32-bit integer.", allocator);
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
var floor_id: ?u32 = null;
|
||||||
|
if (arg_iter.next()) |floor_id_str| {
|
||||||
|
floor_id = std.fmt.parseInt(u32, floor_id_str, 10) catch {
|
||||||
|
try commandhandler.sendMessage(session, "Error: Invalid floor ID. Please provide a valid unsigned 32-bit integer.", allocator);
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
var tp_msg = try std.fmt.allocPrint(allocator, "Teleporting to entry ID: {d}", .{entry_id});
|
||||||
|
if (plane_id) |pid| {
|
||||||
|
tp_msg = try std.fmt.allocPrint(allocator, "{s}, plane ID: {d}", .{ tp_msg, pid });
|
||||||
|
}
|
||||||
|
if (floor_id) |fid| {
|
||||||
|
tp_msg = try std.fmt.allocPrint(allocator, "{s}, floor ID: {d}", .{ tp_msg, fid });
|
||||||
|
}
|
||||||
|
|
||||||
|
try commandhandler.sendMessage(session, std.fmt.allocPrint(allocator, "Teleporting to entry ID: {d} {any} {any}\n", .{ entry_id, plane_id, floor_id }) catch "Error formatting message", allocator);
|
||||||
|
|
||||||
|
var planeID: u32 = 0;
|
||||||
|
var floorID: u32 = 0;
|
||||||
|
if (plane_id) |pid| planeID = pid;
|
||||||
|
if (floor_id) |fid| floorID = fid;
|
||||||
|
var scene_manager = SceneManager.init(allocator);
|
||||||
|
const scene_info = try scene_manager.createScene(planeID, floorID, entry_id, 0);
|
||||||
|
var lineup_mgr = LineupManager.init(allocator);
|
||||||
|
const lineup = try lineup_mgr.createLineup();
|
||||||
|
try session.send(CmdID.CmdEnterSceneByServerScNotify, protocol.EnterSceneByServerScNotify{
|
||||||
|
.reason = protocol.EnterSceneReason.ENTER_SCENE_REASON_NONE,
|
||||||
|
.lineup = lineup,
|
||||||
|
.scene = scene_info,
|
||||||
|
});
|
||||||
|
}
|
23
gameserver/src/commands/unstuck.zig
Normal file
23
gameserver/src/commands/unstuck.zig
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
const commandhandler = @import("../command.zig");
|
||||||
|
const std = @import("std");
|
||||||
|
const Session = @import("../Session.zig");
|
||||||
|
const protocol = @import("protocol");
|
||||||
|
const LineupManager = @import("../manager/lineup_mgr.zig").LineupManager;
|
||||||
|
const SceneManager = @import("../manager/scene_mgr.zig").SceneManager;
|
||||||
|
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
const CmdID = protocol.CmdID;
|
||||||
|
const Error = commandhandler.Error;
|
||||||
|
|
||||||
|
pub fn handle(session: *Session, _: []const u8, allocator: Allocator) Error!void {
|
||||||
|
var lineup_mgr = LineupManager.init(allocator);
|
||||||
|
const lineup = try lineup_mgr.createLineup();
|
||||||
|
var scene_manager = SceneManager.init(allocator);
|
||||||
|
const scene_info = try scene_manager.createScene(20421, 20421001, 2042101, 2042106);
|
||||||
|
|
||||||
|
try session.send(CmdID.CmdEnterSceneByServerScNotify, protocol.EnterSceneByServerScNotify{
|
||||||
|
.reason = protocol.EnterSceneReason.ENTER_SCENE_REASON_DIMENSION_MERGE,
|
||||||
|
.lineup = lineup,
|
||||||
|
.scene = scene_info,
|
||||||
|
});
|
||||||
|
}
|
124
gameserver/src/commands/value.zig
Normal file
124
gameserver/src/commands/value.zig
Normal file
|
@ -0,0 +1,124 @@
|
||||||
|
const commandhandler = @import("../command.zig");
|
||||||
|
const std = @import("std");
|
||||||
|
const Session = @import("../Session.zig");
|
||||||
|
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
const Error = commandhandler.Error;
|
||||||
|
|
||||||
|
pub var challenge_node: u32 = 0;
|
||||||
|
pub var StandardBanner = [_]u32{ 1003, 1004, 1101, 1104, 1209, 1211 };
|
||||||
|
pub var RateUp = [_]u32{1406};
|
||||||
|
pub var RateUpFourStars = [_]u32{ 1210, 1108, 1207 };
|
||||||
|
|
||||||
|
pub fn handle(session: *Session, _: []const u8, allocator: Allocator) Error!void {
|
||||||
|
try commandhandler.sendMessage(session, "Test Command for Chat\n", allocator);
|
||||||
|
}
|
||||||
|
pub fn challengeNode(session: *Session, _: []const u8, allocator: Allocator) Error!void {
|
||||||
|
if (challenge_node == 0) {
|
||||||
|
try commandhandler.sendMessage(session, "Change Challenge Node 2 \n", allocator);
|
||||||
|
challenge_node = challenge_node + 1;
|
||||||
|
} else {
|
||||||
|
try commandhandler.sendMessage(session, "Change Challenge Node 1 \n", allocator);
|
||||||
|
challenge_node = challenge_node - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn setGachaCommand(session: *Session, args: []const u8, allocator: Allocator) Error!void {
|
||||||
|
var arg_iter = std.mem.split(u8, args, " ");
|
||||||
|
const command = arg_iter.next() orelse {
|
||||||
|
try commandhandler.sendMessage(session, "Error: Missing sub-command. Usage: /set <sub-command> [arguments]", allocator);
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
if (std.mem.eql(u8, command, "standard")) {
|
||||||
|
try standard(session, &arg_iter, allocator);
|
||||||
|
} else if (std.mem.eql(u8, command, "rateup")) {
|
||||||
|
const next = arg_iter.next();
|
||||||
|
if (next) |rateup_number| {
|
||||||
|
if (std.mem.eql(u8, rateup_number, "5")) {
|
||||||
|
try gacha5Stars(session, &arg_iter, allocator);
|
||||||
|
} else if (std.mem.eql(u8, rateup_number, "4")) {
|
||||||
|
try gacha4Stars(session, &arg_iter, allocator);
|
||||||
|
} else {
|
||||||
|
try commandhandler.sendMessage(session, "Error: Invalid rateup number. Please use 4 (four stars) or 5 (5 stars).", allocator);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try commandhandler.sendMessage(session, "Error: Missing number for rateup. Usage: /set rateup <number>", allocator);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try commandhandler.sendMessage(session, "Error: Unknown sub-command. Available: standard, rateup 5, rateup 4", allocator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn standard(session: *Session, arg_iter: *std.mem.SplitIterator(u8, .sequence), allocator: Allocator) Error!void {
|
||||||
|
var avatar_ids: [6]u32 = undefined;
|
||||||
|
var count: usize = 0;
|
||||||
|
while (count < 6) {
|
||||||
|
if (arg_iter.next()) |avatar_id_str| {
|
||||||
|
const id = std.fmt.parseInt(u32, avatar_id_str, 10) catch {
|
||||||
|
return sendErrorMessage(session, "Error: Invalid avatar ID. Please provide a valid unsigned 32-bit integer.", allocator);
|
||||||
|
};
|
||||||
|
if (!isValidAvatarId(id)) {
|
||||||
|
return sendErrorMessage(session, "Error: Invalid Avatar ID format.", allocator);
|
||||||
|
}
|
||||||
|
avatar_ids[count] = id;
|
||||||
|
count += 1;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (arg_iter.next() != null or count != 6) {
|
||||||
|
return sendErrorMessage(session, "Error: You must provide exactly 6 avatar IDs.", allocator);
|
||||||
|
}
|
||||||
|
@memcpy(&StandardBanner, &avatar_ids);
|
||||||
|
const msg = try std.fmt.allocPrint(allocator, "Set standard banner ID to: {d}, {d}, {d}, {d}, {d}, {d}", .{ avatar_ids[0], avatar_ids[1], avatar_ids[2], avatar_ids[3], avatar_ids[4], avatar_ids[5] });
|
||||||
|
try commandhandler.sendMessage(session, msg, allocator);
|
||||||
|
}
|
||||||
|
fn gacha4Stars(session: *Session, arg_iter: *std.mem.SplitIterator(u8, .sequence), allocator: Allocator) Error!void {
|
||||||
|
var avatar_ids: [3]u32 = undefined;
|
||||||
|
var count: usize = 0;
|
||||||
|
while (count < 3) {
|
||||||
|
if (arg_iter.next()) |avatar_id_str| {
|
||||||
|
const id = std.fmt.parseInt(u32, avatar_id_str, 10) catch {
|
||||||
|
return sendErrorMessage(session, "Error: Invalid avatar ID. Please provide a valid unsigned 32-bit integer.", allocator);
|
||||||
|
};
|
||||||
|
if (!isValidAvatarId(id)) {
|
||||||
|
return sendErrorMessage(session, "Error: Invalid Avatar ID format.", allocator);
|
||||||
|
}
|
||||||
|
avatar_ids[count] = id;
|
||||||
|
count += 1;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (arg_iter.next() != null or count != 3) {
|
||||||
|
return sendErrorMessage(session, "Error: You must provide exactly 3 avatar IDs.", allocator);
|
||||||
|
}
|
||||||
|
@memcpy(&RateUpFourStars, &avatar_ids);
|
||||||
|
const msg = try std.fmt.allocPrint(allocator, "Set 4 star rate up ID to: {d}, {d}, {d}", .{ avatar_ids[0], avatar_ids[1], avatar_ids[2] });
|
||||||
|
try commandhandler.sendMessage(session, msg, allocator);
|
||||||
|
}
|
||||||
|
fn gacha5Stars(session: *Session, arg_iter: *std.mem.SplitIterator(u8, .sequence), allocator: Allocator) Error!void {
|
||||||
|
var avatar_ids: [1]u32 = undefined;
|
||||||
|
if (arg_iter.next()) |avatar_id_str| {
|
||||||
|
const id = std.fmt.parseInt(u32, avatar_id_str, 10) catch {
|
||||||
|
return sendErrorMessage(session, "Error: Invalid avatar ID. Please provide a valid unsigned 32-bit integer.", allocator);
|
||||||
|
};
|
||||||
|
if (!isValidAvatarId(id)) {
|
||||||
|
return sendErrorMessage(session, "Error: Invalid Avatar ID format.", allocator);
|
||||||
|
}
|
||||||
|
avatar_ids[0] = id;
|
||||||
|
} else {
|
||||||
|
return sendErrorMessage(session, "Error: You must provide a rate-up avatar ID.", allocator);
|
||||||
|
}
|
||||||
|
if (arg_iter.next() != null) {
|
||||||
|
return sendErrorMessage(session, "Error: Only one rate-up avatar ID is allowed.", allocator);
|
||||||
|
}
|
||||||
|
@memcpy(&RateUp, &avatar_ids);
|
||||||
|
const msg = try std.fmt.allocPrint(allocator, "Set rate up ID to: {d}", .{avatar_ids[0]});
|
||||||
|
try commandhandler.sendMessage(session, msg, allocator);
|
||||||
|
}
|
||||||
|
fn sendErrorMessage(session: *Session, message: []const u8, allocator: Allocator) Error!void {
|
||||||
|
try commandhandler.sendMessage(session, message, allocator);
|
||||||
|
}
|
||||||
|
fn isValidAvatarId(avatar_id: u32) bool {
|
||||||
|
return avatar_id >= 1000 and avatar_id <= 9999;
|
||||||
|
}
|
257
gameserver/src/data.zig
Normal file
257
gameserver/src/data.zig
Normal file
File diff suppressed because one or more lines are too long
251
gameserver/src/handlers.zig
Normal file
251
gameserver/src/handlers.zig
Normal file
|
@ -0,0 +1,251 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const protocol = @import("protocol");
|
||||||
|
const Session = @import("Session.zig");
|
||||||
|
const Packet = @import("Packet.zig");
|
||||||
|
const avatar = @import("services/avatar.zig");
|
||||||
|
const chat = @import("services/chat.zig");
|
||||||
|
const gacha = @import("services/gacha.zig");
|
||||||
|
const item = @import("services/item.zig");
|
||||||
|
const battle = @import("services/battle.zig");
|
||||||
|
const login = @import("services/login.zig");
|
||||||
|
const lineup = @import("services/lineup.zig");
|
||||||
|
const mail = @import("services/mail.zig");
|
||||||
|
const misc = @import("services/misc.zig");
|
||||||
|
const mission = @import("services/mission.zig");
|
||||||
|
const pet = @import("services/pet.zig");
|
||||||
|
const profile = @import("services/profile.zig");
|
||||||
|
const scene = @import("services/scene.zig");
|
||||||
|
const events = @import("services/events.zig");
|
||||||
|
const challenge = @import("services/challenge.zig");
|
||||||
|
const multipath = @import("services/multipath.zig");
|
||||||
|
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
const ArenaAllocator = std.heap.ArenaAllocator;
|
||||||
|
const CmdID = protocol.CmdID;
|
||||||
|
|
||||||
|
const log = std.log.scoped(.handlers);
|
||||||
|
|
||||||
|
const Action = *const fn (*Session, *const Packet, Allocator) anyerror!void;
|
||||||
|
const HandlerList = [_]struct { CmdID, Action }{
|
||||||
|
.{ CmdID.CmdPlayerGetTokenCsReq, login.onPlayerGetToken },
|
||||||
|
.{ CmdID.CmdPlayerLoginCsReq, login.onPlayerLogin },
|
||||||
|
.{ CmdID.CmdPlayerHeartBeatCsReq, misc.onPlayerHeartBeat },
|
||||||
|
.{ CmdID.CmdPlayerLoginFinishCsReq, login.onPlayerLoginFinish },
|
||||||
|
.{ CmdID.CmdContentPackageGetDataCsReq, login.onContentPackageGetData },
|
||||||
|
.{ CmdID.CmdSetClientPausedCsReq, login.onSetClientPaused },
|
||||||
|
//avatar
|
||||||
|
.{ CmdID.CmdGetAvatarDataCsReq, avatar.onGetAvatarData },
|
||||||
|
.{ CmdID.CmdSetAvatarPathCsReq, avatar.onSetAvatarPath },
|
||||||
|
.{ CmdID.CmdGetBasicInfoCsReq, avatar.onGetBasicInfo },
|
||||||
|
.{ CmdID.CmdGetMultiPathAvatarInfoCsReq, multipath.onGetMultiPathAvatarInfo },
|
||||||
|
.{ CmdID.CmdTakeOffAvatarSkinCsReq, avatar.onTakeOffAvatarSkin },
|
||||||
|
.{ CmdID.CmdDressAvatarSkinCsReq, avatar.onDressAvatarSkin },
|
||||||
|
.{ CmdID.CmdGetBigDataAllRecommendCsReq, avatar.onGetBigDataAll },
|
||||||
|
.{ CmdID.CmdGetBigDataRecommendCsReq, avatar.onGetBigData },
|
||||||
|
//bag
|
||||||
|
.{ CmdID.CmdGetBagCsReq, item.onGetBag },
|
||||||
|
.{ CmdID.CmdUseItemCsReq, item.onUseItem },
|
||||||
|
//lineup
|
||||||
|
.{ CmdID.CmdChangeLineupLeaderCsReq, lineup.onChangeLineupLeader },
|
||||||
|
.{ CmdID.CmdReplaceLineupCsReq, lineup.onReplaceLineup },
|
||||||
|
.{ CmdID.CmdGetCurLineupDataCsReq, lineup.onGetCurLineupData },
|
||||||
|
//battle
|
||||||
|
.{ CmdID.CmdStartCocoonStageCsReq, battle.onStartCocoonStage },
|
||||||
|
.{ CmdID.CmdPVEBattleResultCsReq, battle.onPVEBattleResult },
|
||||||
|
.{ CmdID.CmdSceneCastSkillCsReq, battle.onSceneCastSkill },
|
||||||
|
.{ CmdID.CmdSceneCastSkillCostMpCsReq, battle.onSceneCastSkillCostMp },
|
||||||
|
.{ CmdID.CmdQuickStartCocoonStageCsReq, battle.onQuickStartCocoonStage },
|
||||||
|
.{ CmdID.CmdQuickStartFarmElementCsReq, battle.onQuickStartFarmElement },
|
||||||
|
.{ CmdID.CmdStartBattleCollegeCsReq, battle.onStartBattleCollege },
|
||||||
|
//gacha
|
||||||
|
.{ CmdID.CmdGetGachaInfoCsReq, gacha.onGetGachaInfo },
|
||||||
|
.{ CmdID.CmdBuyGoodsCsReq, gacha.onBuyGoods },
|
||||||
|
.{ CmdID.CmdGetShopListCsReq, gacha.onGetShopList },
|
||||||
|
.{ CmdID.CmdExchangeHcoinCsReq, gacha.onExchangeHcoin },
|
||||||
|
.{ CmdID.CmdDoGachaCsReq, gacha.onDoGacha },
|
||||||
|
//mail
|
||||||
|
.{ CmdID.CmdGetMailCsReq, mail.onGetMail },
|
||||||
|
//pet
|
||||||
|
.{ CmdID.CmdGetPetDataCsReq, pet.onGetPetData },
|
||||||
|
.{ CmdID.CmdRecallPetCsReq, pet.onRecallPet },
|
||||||
|
.{ CmdID.CmdSummonPetCsReq, pet.onSummonPet },
|
||||||
|
//profile
|
||||||
|
.{ CmdID.CmdGetPhoneDataCsReq, profile.onGetPhoneData },
|
||||||
|
.{ CmdID.CmdSelectPhoneThemeCsReq, profile.onSelectPhoneTheme },
|
||||||
|
.{ CmdID.CmdSelectChatBubbleCsReq, profile.onSelectChatBubble },
|
||||||
|
.{ CmdID.CmdGetPlayerBoardDataCsReq, profile.onGetPlayerBoardData },
|
||||||
|
.{ CmdID.CmdSetDisplayAvatarCsReq, profile.onSetDisplayAvatar },
|
||||||
|
.{ CmdID.CmdSetAssistAvatarCsReq, profile.onSetAssistAvatar },
|
||||||
|
.{ CmdID.CmdSetSignatureCsReq, profile.onSetSignature },
|
||||||
|
.{ CmdID.CmdSetGameplayBirthdayCsReq, profile.onSetGameplayBirthday },
|
||||||
|
.{ CmdID.CmdSetHeadIconCsReq, profile.onSetHeadIcon },
|
||||||
|
//mission
|
||||||
|
.{ CmdID.CmdGetTutorialGuideCsReq, mission.onGetTutorialGuideStatus },
|
||||||
|
.{ CmdID.CmdGetMissionStatusCsReq, mission.onGetMissionStatus },
|
||||||
|
.{ CmdID.CmdGetTutorialCsReq, mission.onGetTutorialStatus },
|
||||||
|
.{ CmdID.CmdFinishTutorialGuideCsReq, mission.onFinishTutorialGuideStatus },
|
||||||
|
.{ CmdID.CmdUnlockTutorialGuideCsReq, mission.onUnlockTutorialGuide },
|
||||||
|
//chat
|
||||||
|
.{ CmdID.CmdGetFriendListInfoCsReq, chat.onGetFriendListInfo },
|
||||||
|
.{ CmdID.CmdGetPrivateChatHistoryCsReq, chat.onPrivateChatHistory },
|
||||||
|
.{ CmdID.CmdGetChatEmojiListCsReq, chat.onChatEmojiList },
|
||||||
|
.{ CmdID.CmdSendMsgCsReq, chat.onSendMsg },
|
||||||
|
//scene
|
||||||
|
.{ CmdID.CmdGetCurSceneInfoCsReq, scene.onGetCurSceneInfo },
|
||||||
|
.{ CmdID.CmdSceneEntityMoveCsReq, scene.onSceneEntityMove },
|
||||||
|
.{ CmdID.CmdEnterSceneCsReq, scene.onEnterScene },
|
||||||
|
.{ CmdID.CmdGetSceneMapInfoCsReq, scene.onGetSceneMapInfo },
|
||||||
|
.{ CmdID.CmdGetUnlockTeleportCsReq, scene.onGetUnlockTeleport },
|
||||||
|
.{ CmdID.CmdEnterSectionCsReq, scene.onEnterSection },
|
||||||
|
.{ CmdID.CmdSceneEntityTeleportCsReq, scene.onSceneEntityTeleport },
|
||||||
|
.{ CmdID.CmdGetFirstTalkNpcCsReq, scene.onGetFirstTalkNpc },
|
||||||
|
.{ CmdID.CmdGetFirstTalkByPerformanceNpcCsReq, scene.onGetFirstTalkByPerformanceNp },
|
||||||
|
.{ CmdID.CmdGetNpcTakenRewardCsReq, scene.onGetNpcTakenReward },
|
||||||
|
.{ CmdID.CmdUpdateGroupPropertyCsReq, scene.onUpdateGroupProperty },
|
||||||
|
.{ CmdID.CmdChangePropTimelineInfoCsReq, scene.onChangePropTimeline },
|
||||||
|
.{ CmdID.CmdDeactivateFarmElementCsReq, scene.onDeactivateFarmElement },
|
||||||
|
.{ CmdID.CmdSetGroupCustomSaveDataCsReq, scene.onSetGroupCustomSaveData },
|
||||||
|
//events
|
||||||
|
.{ CmdID.CmdGetActivityScheduleConfigCsReq, events.onGetActivity },
|
||||||
|
//challenge
|
||||||
|
.{ CmdID.CmdGetChallengeCsReq, challenge.onGetChallenge },
|
||||||
|
.{ CmdID.CmdGetChallengeGroupStatisticsCsReq, challenge.onGetChallengeGroupStatistics },
|
||||||
|
.{ CmdID.CmdStartChallengeCsReq, challenge.onStartChallenge },
|
||||||
|
.{ CmdID.CmdLeaveChallengeCsReq, challenge.onLeaveChallenge },
|
||||||
|
};
|
||||||
|
|
||||||
|
const DummyCmdList = [_]struct { CmdID, CmdID }{
|
||||||
|
.{ CmdID.CmdGetBagCsReq, CmdID.CmdGetBagScRsp },
|
||||||
|
.{ CmdID.CmdGetMarkItemListCsReq, CmdID.CmdGetMarkItemListScRsp },
|
||||||
|
.{ CmdID.CmdGetPlayerBoardDataCsReq, CmdID.CmdGetPlayerBoardDataScRsp },
|
||||||
|
.{ CmdID.CmdGetCurAssistCsReq, CmdID.CmdGetCurAssistScRsp },
|
||||||
|
.{ CmdID.CmdGetAllLineupDataCsReq, CmdID.CmdGetAllLineupDataScRsp },
|
||||||
|
.{ CmdID.CmdGetAllServerPrefsDataCsReq, CmdID.CmdGetAllServerPrefsDataScRsp },
|
||||||
|
.{ CmdID.CmdGetMissionDataCsReq, CmdID.CmdGetMissionDataScRsp },
|
||||||
|
.{ CmdID.CmdGetQuestDataCsReq, CmdID.CmdGetQuestDataScRsp },
|
||||||
|
.{ CmdID.CmdGetCurChallengeCsReq, CmdID.CmdGetCurChallengeScRsp },
|
||||||
|
.{ CmdID.CmdGetRogueCommonDialogueDataCsReq, CmdID.CmdGetRogueCommonDialogueDataScRsp },
|
||||||
|
.{ CmdID.CmdGetRogueInfoCsReq, CmdID.CmdGetRogueInfoScRsp },
|
||||||
|
.{ CmdID.CmdGetRogueHandbookDataCsReq, CmdID.CmdGetRogueHandbookDataScRsp },
|
||||||
|
.{ CmdID.CmdGetRogueEndlessActivityDataCsReq, CmdID.CmdGetRogueEndlessActivityDataScRsp },
|
||||||
|
.{ CmdID.CmdChessRogueQueryCsReq, CmdID.CmdChessRogueQueryScRsp },
|
||||||
|
.{ CmdID.CmdRogueTournQueryCsReq, CmdID.CmdRogueTournQueryScRsp },
|
||||||
|
.{ CmdID.CmdSyncClientResVersionCsReq, CmdID.CmdSyncClientResVersionScRsp },
|
||||||
|
.{ CmdID.CmdDailyFirstMeetPamCsReq, CmdID.CmdDailyFirstMeetPamScRsp },
|
||||||
|
.{ CmdID.CmdGetBattleCollegeDataCsReq, CmdID.CmdGetBattleCollegeDataScRsp },
|
||||||
|
.{ CmdID.CmdGetNpcStatusCsReq, CmdID.CmdGetNpcStatusScRsp },
|
||||||
|
.{ CmdID.CmdGetSecretKeyInfoCsReq, CmdID.CmdGetSecretKeyInfoScRsp },
|
||||||
|
.{ CmdID.CmdGetHeartDialInfoCsReq, CmdID.CmdGetHeartDialInfoScRsp },
|
||||||
|
.{ CmdID.CmdGetVideoVersionKeyCsReq, CmdID.CmdGetVideoVersionKeyScRsp },
|
||||||
|
.{ CmdID.CmdGetCurBattleInfoCsReq, CmdID.CmdGetCurBattleInfoScRsp },
|
||||||
|
.{ CmdID.CmdHeliobusActivityDataCsReq, CmdID.CmdHeliobusActivityDataScRsp },
|
||||||
|
.{ CmdID.CmdGetAetherDivideInfoCsReq, CmdID.CmdGetAetherDivideInfoScRsp },
|
||||||
|
.{ CmdID.CmdGetMapRotationDataCsReq, CmdID.CmdGetMapRotationDataScRsp },
|
||||||
|
.{ CmdID.CmdGetRogueCollectionCsReq, CmdID.CmdGetRogueCollectionScRsp },
|
||||||
|
.{ CmdID.CmdGetRogueExhibitionCsReq, CmdID.CmdGetRogueExhibitionScRsp },
|
||||||
|
.{ CmdID.CmdPlayerReturnInfoQueryCsReq, CmdID.CmdPlayerReturnInfoQueryScRsp },
|
||||||
|
.{ CmdID.CmdGetLevelRewardTakenListCsReq, CmdID.CmdGetLevelRewardTakenListScRsp },
|
||||||
|
.{ CmdID.CmdGetMainMissionCustomValueCsReq, CmdID.CmdGetMainMissionCustomValueScRsp },
|
||||||
|
.{ CmdID.CmdGetMaterialSubmitActivityDataCsReq, CmdID.CmdGetMaterialSubmitActivityDataScRsp },
|
||||||
|
.{ CmdID.CmdRogueTournGetCurRogueCocoonInfoCsReq, CmdID.CmdRogueTournGetCurRogueCocoonInfoScRsp },
|
||||||
|
.{ CmdID.CmdRogueMagicQueryCsReq, CmdID.CmdRogueMagicQueryScRsp },
|
||||||
|
.{ CmdID.CmdMusicRhythmDataCsReq, CmdID.CmdMusicRhythmDataScRsp },
|
||||||
|
//friendlist
|
||||||
|
.{ CmdID.CmdGetFriendApplyListInfoCsReq, CmdID.CmdGetFriendApplyListInfoScRsp },
|
||||||
|
.{ CmdID.CmdGetChatFriendHistoryCsReq, CmdID.CmdGetChatFriendHistoryScRsp },
|
||||||
|
.{ CmdID.CmdGetFriendLoginInfoCsReq, CmdID.CmdGetFriendLoginInfoScRsp },
|
||||||
|
.{ CmdID.CmdGetFriendBattleRecordDetailCsReq, CmdID.CmdGetFriendBattleRecordDetailScRsp },
|
||||||
|
.{ CmdID.CmdGetFriendDevelopmentInfoCsReq, CmdID.CmdGetFriendDevelopmentInfoScRsp },
|
||||||
|
.{ CmdID.CmdGetFriendRecommendListInfoCsReq, CmdID.CmdGetFriendRecommendListInfoScRsp },
|
||||||
|
//add
|
||||||
|
.{ CmdID.CmdSwitchHandDataCsReq, CmdID.CmdSwitchHandDataScRsp },
|
||||||
|
.{ CmdID.CmdRogueArcadeGetInfoCsReq, CmdID.CmdRogueArcadeGetInfoScRsp },
|
||||||
|
.{ CmdID.CmdGetMissionMessageInfoCsReq, CmdID.CmdGetMissionMessageInfoScRsp },
|
||||||
|
.{ CmdID.CmdTrainPartyGetDataCsReq, CmdID.CmdTrainPartyGetDataScRsp },
|
||||||
|
.{ CmdID.CmdGetEnteredSceneCsReq, CmdID.CmdGetEnteredSceneScRsp },
|
||||||
|
.{ CmdID.CmdQueryProductInfoCsReq, CmdID.CmdQueryProductInfoScRsp },
|
||||||
|
.{ CmdID.CmdGetPamSkinDataCsReq, CmdID.CmdGetPamSkinDataScRsp },
|
||||||
|
.{ CmdID.CmdGetRogueScoreRewardInfoCsReq, CmdID.CmdGetRogueScoreRewardInfoScRsp },
|
||||||
|
.{ CmdID.CmdGetQuestRecordCsReq, CmdID.CmdGetQuestRecordScRsp },
|
||||||
|
.{ CmdID.CmdGetDailyActiveInfoCsReq, CmdID.CmdGetDailyActiveInfoScRsp },
|
||||||
|
.{ CmdID.CmdGetChessRogueNousStoryInfoCsReq, CmdID.CmdGetChessRogueNousStoryInfoScRsp },
|
||||||
|
.{ CmdID.CmdCommonRogueQueryCsReq, CmdID.CmdCommonRogueQueryScRsp },
|
||||||
|
.{ CmdID.CmdGetFightActivityDataCsReq, CmdID.CmdGetFightActivityDataScRsp },
|
||||||
|
.{ CmdID.CmdGetStarFightDataCsReq, CmdID.CmdGetStarFightDataScRsp },
|
||||||
|
.{ CmdID.CmdGetMultipleDropInfoCsReq, CmdID.CmdGetMultipleDropInfoScRsp },
|
||||||
|
.{ CmdID.CmdGetPlayerReturnMultiDropInfoCsReq, CmdID.CmdGetPlayerReturnMultiDropInfoScRsp },
|
||||||
|
.{ CmdID.CmdGetShareDataCsReq, CmdID.CmdGetShareDataScRsp },
|
||||||
|
.{ CmdID.CmdGetTreasureDungeonActivityDataCsReq, CmdID.CmdGetTreasureDungeonActivityDataScRsp },
|
||||||
|
.{ CmdID.CmdEvolveBuildQueryInfoCsReq, CmdID.CmdEvolveBuildQueryInfoScRsp },
|
||||||
|
.{ CmdID.CmdGetAlleyInfoCsReq, CmdID.CmdGetAlleyInfoScRsp },
|
||||||
|
.{ CmdID.CmdGetAetherDivideChallengeInfoCsReq, CmdID.CmdGetAetherDivideChallengeInfoScRsp },
|
||||||
|
.{ CmdID.CmdGetStrongChallengeActivityDataCsReq, CmdID.CmdGetStrongChallengeActivityDataScRsp },
|
||||||
|
.{ CmdID.CmdGetOfferingInfoCsReq, CmdID.CmdGetOfferingInfoScRsp },
|
||||||
|
.{ CmdID.CmdClockParkGetInfoCsReq, CmdID.CmdClockParkGetInfoScRsp },
|
||||||
|
.{ CmdID.CmdGetGunPlayDataCsReq, CmdID.CmdGetGunPlayDataScRsp },
|
||||||
|
.{ CmdID.CmdGetTrackPhotoActivityDataCsReq, CmdID.CmdGetTrackPhotoActivityDataScRsp },
|
||||||
|
.{ CmdID.CmdGetSwordTrainingDataCsReq, CmdID.CmdGetSwordTrainingDataScRsp },
|
||||||
|
.{ CmdID.CmdGetFightFestDataCsReq, CmdID.CmdGetFightFestDataScRsp },
|
||||||
|
.{ CmdID.CmdDifficultyAdjustmentGetDataCsReq, CmdID.CmdDifficultyAdjustmentGetDataScRsp },
|
||||||
|
.{ CmdID.CmdSpaceZooDataCsReq, CmdID.CmdSpaceZooDataScRsp },
|
||||||
|
.{ CmdID.CmdGetExpeditionDataCsReq, CmdID.CmdGetExpeditionDataScRsp },
|
||||||
|
.{ CmdID.CmdTravelBrochureGetDataCsReq, CmdID.CmdTravelBrochureGetDataScRsp },
|
||||||
|
.{ CmdID.CmdRaidCollectionDataCsReq, CmdID.CmdRaidCollectionDataScRsp },
|
||||||
|
.{ CmdID.CmdGetRaidInfoCsReq, CmdID.CmdGetRaidInfoScRsp },
|
||||||
|
.{ CmdID.CmdGetLoginActivityCsReq, CmdID.CmdGetLoginActivityScRsp },
|
||||||
|
.{ CmdID.CmdGetTrialActivityDataCsReq, CmdID.CmdGetTrialActivityDataScRsp },
|
||||||
|
.{ CmdID.CmdGetJukeboxDataCsReq, CmdID.CmdGetJukeboxDataScRsp },
|
||||||
|
.{ CmdID.CmdGetMuseumInfoCsReq, CmdID.CmdGetMuseumInfoScRsp },
|
||||||
|
.{ CmdID.CmdGetTelevisionActivityDataCsReq, CmdID.CmdGetTelevisionActivityDataScRsp },
|
||||||
|
.{ CmdID.CmdGetTrainVisitorRegisterCsReq, CmdID.CmdGetTrainVisitorRegisterScRsp },
|
||||||
|
.{ CmdID.CmdGetBoxingClubInfoCsReq, CmdID.CmdGetBoxingClubInfoScRsp },
|
||||||
|
.{ CmdID.CmdTextJoinQueryCsReq, CmdID.CmdTextJoinQueryScRsp },
|
||||||
|
.{ CmdID.CmdGetLoginChatInfoCsReq, CmdID.CmdGetLoginChatInfoScRsp },
|
||||||
|
.{ CmdID.CmdGetFeverTimeActivityDataCsReq, CmdID.CmdGetFeverTimeActivityDataScRsp },
|
||||||
|
.{ CmdID.CmdGetSummonActivityDataCsReq, CmdID.CmdGetSummonActivityDataScRsp },
|
||||||
|
.{ CmdID.CmdTarotBookGetDataCsReq, CmdID.CmdTarotBookGetDataScRsp },
|
||||||
|
.{ CmdID.CmdGetMarkChestCsReq, CmdID.CmdGetMarkChestScRsp },
|
||||||
|
.{ CmdID.CmdMatchThreeGetDataCsReq, CmdID.CmdMatchThreeGetDataScRsp },
|
||||||
|
.{ CmdID.CmdUpdateServerPrefsDataCsReq, CmdID.CmdUpdateServerPrefsDataScRsp },
|
||||||
|
.{ CmdID.CmdUpdateTrackMainMissionIdCsReq, CmdID.CmdUpdateTrackMainMissionIdScRsp },
|
||||||
|
.{ CmdID.CmdGetNpcMessageGroupCsReq, CmdID.CmdGetNpcMessageGroupScRsp },
|
||||||
|
.{ CmdID.CmdGetAllSaveRaidCsReq, CmdID.CmdGetAllSaveRaidScRsp },
|
||||||
|
.{ CmdID.CmdGetAssistHistoryCsReq, CmdID.CmdGetAssistHistoryScRsp },
|
||||||
|
.{ CmdID.CmdGetPlayerDetailInfoCsReq, CmdID.CmdGetPlayerDetailInfoScRsp },
|
||||||
|
.{ CmdID.CmdGetEraFlipperDataCsReq, CmdID.CmdGetEraFlipperDataScRsp },
|
||||||
|
.{ CmdID.CmdGetPreAvatarListCsReq, CmdID.CmdGetPreAvatarListScRsp },
|
||||||
|
.{ CmdID.CmdGetRechargeGiftInfoCsReq, CmdID.CmdGetRechargeGiftInfoScRsp },
|
||||||
|
.{ CmdID.CmdGetRechargeBenefitInfoCsReq, CmdID.CmdGetRechargeBenefitInfoScRsp },
|
||||||
|
.{ CmdID.CmdRelicSmartWearGetPlanCsReq, CmdID.CmdRelicSmartWearGetPlanScRsp },
|
||||||
|
.{ CmdID.CmdRelicSmartWearGetPinRelicCsReq, CmdID.CmdRelicSmartWearGetPinRelicScRsp },
|
||||||
|
.{ CmdID.CmdSetGrowthTargetAvatarCsReq, CmdID.CmdSetGrowthTargetAvatarScRsp },
|
||||||
|
//.{ CmdID.CmdGetStoryTokenActivityDataCsReq, CmdID.CmdGetStoryTokenActivityDataScRsp },
|
||||||
|
};
|
||||||
|
|
||||||
|
const SuppressLogList = [_]CmdID{CmdID.CmdSceneEntityMoveCsReq};
|
||||||
|
|
||||||
|
pub fn handle(session: *Session, packet: *const Packet) !void {
|
||||||
|
var arena = ArenaAllocator.init(session.allocator);
|
||||||
|
defer arena.deinit();
|
||||||
|
|
||||||
|
const cmd_id: CmdID = @enumFromInt(packet.cmd_id);
|
||||||
|
|
||||||
|
inline for (HandlerList) |handler| {
|
||||||
|
if (handler[0] == cmd_id) {
|
||||||
|
try handler[1](session, packet, arena.allocator());
|
||||||
|
if (!std.mem.containsAtLeast(CmdID, &SuppressLogList, 1, &[_]CmdID{cmd_id})) {
|
||||||
|
log.debug("packet {} was handled", .{cmd_id});
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline for (DummyCmdList) |pair| {
|
||||||
|
if (pair[0] == cmd_id) {
|
||||||
|
try session.send_empty(pair[1]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.warn("packet {} was ignored", .{cmd_id});
|
||||||
|
}
|
15
gameserver/src/main.zig
Normal file
15
gameserver/src/main.zig
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const builtin = @import("builtin");
|
||||||
|
const network = @import("network.zig");
|
||||||
|
const handlers = @import("handlers.zig");
|
||||||
|
|
||||||
|
pub const std_options = .{
|
||||||
|
.log_level = switch (builtin.mode) {
|
||||||
|
.Debug => .debug,
|
||||||
|
else => .info,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn main() !void {
|
||||||
|
try network.listen();
|
||||||
|
}
|
254
gameserver/src/manager/battle_mgr.zig
Normal file
254
gameserver/src/manager/battle_mgr.zig
Normal file
|
@ -0,0 +1,254 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const protocol = @import("protocol");
|
||||||
|
const Session = @import("../Session.zig");
|
||||||
|
const Packet = @import("../Packet.zig");
|
||||||
|
const Config = @import("../services/config.zig");
|
||||||
|
const Data = @import("../data.zig");
|
||||||
|
|
||||||
|
const ArrayList = std.ArrayList;
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
const CmdID = protocol.CmdID;
|
||||||
|
|
||||||
|
// function to check the list if true
|
||||||
|
fn isInList(id: u32, list: []const u32) bool {
|
||||||
|
for (list) |item| {
|
||||||
|
if (item == id) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
pub const BattleManager = struct {
|
||||||
|
allocator: std.mem.Allocator,
|
||||||
|
pub fn init(allocator: std.mem.Allocator) BattleManager {
|
||||||
|
return BattleManager{ .allocator = allocator };
|
||||||
|
}
|
||||||
|
pub fn createBattle(self: *BattleManager) !protocol.SceneBattleInfo {
|
||||||
|
const config = try Config.loadGameConfig(self.allocator, "config.json");
|
||||||
|
|
||||||
|
var battle = protocol.SceneBattleInfo.init(self.allocator);
|
||||||
|
const BattleBuff = protocol.BattleBuff;
|
||||||
|
|
||||||
|
// Avatar handler
|
||||||
|
for (config.avatar_config.items, 0..) |avatarConf, idx| {
|
||||||
|
var avatar = protocol.BattleAvatar.init(self.allocator);
|
||||||
|
avatar.id = avatarConf.id;
|
||||||
|
avatar.hp = avatarConf.hp * 100;
|
||||||
|
avatar.sp_bar = .{ .sp_cur = avatarConf.sp * 100, .sp_max = 10000 };
|
||||||
|
avatar.level = avatarConf.level;
|
||||||
|
avatar.rank = avatarConf.rank;
|
||||||
|
avatar.promotion = avatarConf.promotion;
|
||||||
|
avatar.avatar_type = .AVATAR_FORMAL_TYPE;
|
||||||
|
|
||||||
|
// Relics
|
||||||
|
for (avatarConf.relics.items) |relic| {
|
||||||
|
const r = try relicCoder(self.allocator, relic.id, relic.level, relic.main_affix_id, relic.stat1, relic.cnt1, relic.step1, relic.stat2, relic.cnt2, relic.step2, relic.stat3, relic.cnt3, relic.step3, relic.stat4, relic.cnt4, relic.step4);
|
||||||
|
try avatar.relic_list.append(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lightcone (LC)
|
||||||
|
const lc = protocol.BattleEquipment{
|
||||||
|
.id = avatarConf.lightcone.id,
|
||||||
|
.rank = avatarConf.lightcone.rank,
|
||||||
|
.level = avatarConf.lightcone.level,
|
||||||
|
.promotion = avatarConf.lightcone.promotion,
|
||||||
|
};
|
||||||
|
try avatar.equipment_list.append(lc);
|
||||||
|
//max trace
|
||||||
|
var talentLevel: u32 = 0;
|
||||||
|
const skill_list: []const u32 = if (isInList(avatar.id, &Data.Rem)) &Data.skills else &Data.skills_old;
|
||||||
|
for (skill_list) |elem| {
|
||||||
|
talentLevel = switch (elem) {
|
||||||
|
1 => 6,
|
||||||
|
2...4 => 10,
|
||||||
|
301, 302 => if (isInList(avatar.id, &Data.Rem)) 6 else 1,
|
||||||
|
else => 1,
|
||||||
|
};
|
||||||
|
const talent = protocol.AvatarSkillTree{ .point_id = avatar.id * 1000 + elem, .level = talentLevel };
|
||||||
|
try avatar.skilltree_list.append(talent);
|
||||||
|
}
|
||||||
|
// enable technique
|
||||||
|
if (avatarConf.use_technique) {
|
||||||
|
var targetIndexList = ArrayList(u32).init(self.allocator);
|
||||||
|
try targetIndexList.append(0);
|
||||||
|
//Add new ID when modifying for new patch
|
||||||
|
|
||||||
|
var buffedAvatarId = avatar.id;
|
||||||
|
if (avatar.id == 8004) {
|
||||||
|
buffedAvatarId = 8003;
|
||||||
|
} else if (avatar.id == 8006) {
|
||||||
|
buffedAvatarId = 8005;
|
||||||
|
} else if (avatar.id == 8008) {
|
||||||
|
buffedAvatarId = 8007;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Data.buffs_unlocked) |buffId| {
|
||||||
|
const idPrefix = buffId / 100;
|
||||||
|
if (idPrefix == buffedAvatarId) {
|
||||||
|
var buff = BattleBuff{
|
||||||
|
.id = buffId,
|
||||||
|
.level = 1,
|
||||||
|
.owner_index = @intCast(idx),
|
||||||
|
.wave_flag = 1,
|
||||||
|
.target_index_list = targetIndexList,
|
||||||
|
.dynamic_values = ArrayList(protocol.BattleBuff.DynamicValuesEntry).init(self.allocator),
|
||||||
|
};
|
||||||
|
|
||||||
|
try buff.dynamic_values.append(.{ .key = .{ .Const = "SkillIndex" }, .value = 0 });
|
||||||
|
try battle.buff_list.append(buff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isInList(buffedAvatarId, &Data.IgnoreToughness)) {
|
||||||
|
var buff_tough = BattleBuff{
|
||||||
|
.id = 1000119, //for is_ignore toughness
|
||||||
|
.level = 1,
|
||||||
|
.owner_index = @intCast(idx),
|
||||||
|
.wave_flag = 1,
|
||||||
|
.target_index_list = targetIndexList,
|
||||||
|
.dynamic_values = ArrayList(protocol.BattleBuff.DynamicValuesEntry).init(self.allocator),
|
||||||
|
};
|
||||||
|
try buff_tough.dynamic_values.append(.{ .key = .{ .Const = "SkillIndex" }, .value = 0 });
|
||||||
|
try battle.buff_list.append(buff_tough);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buffedAvatarId == 1224) {
|
||||||
|
var buff_march = protocol.BattleBuff{
|
||||||
|
.id = 122401, //for hunt march 7th tech
|
||||||
|
.level = 1,
|
||||||
|
.owner_index = @intCast(idx),
|
||||||
|
.wave_flag = 1,
|
||||||
|
.target_index_list = targetIndexList,
|
||||||
|
.dynamic_values = ArrayList(protocol.BattleBuff.DynamicValuesEntry).init(self.allocator),
|
||||||
|
};
|
||||||
|
|
||||||
|
try buff_march.dynamic_values.appendSlice(&[_]protocol.BattleBuff.DynamicValuesEntry{
|
||||||
|
.{ .key = .{ .Const = "#ADF_1" }, .value = 3 },
|
||||||
|
.{ .key = .{ .Const = "#ADF_2" }, .value = 3 },
|
||||||
|
});
|
||||||
|
try battle.buff_list.append(buff_march);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buffedAvatarId == 1310) {
|
||||||
|
var buff_firefly = BattleBuff{
|
||||||
|
.id = 1000112, //for firefly tech
|
||||||
|
.level = 1,
|
||||||
|
.owner_index = @intCast(idx),
|
||||||
|
.wave_flag = 1,
|
||||||
|
.target_index_list = targetIndexList,
|
||||||
|
.dynamic_values = ArrayList(protocol.BattleBuff.DynamicValuesEntry).init(self.allocator),
|
||||||
|
};
|
||||||
|
try buff_firefly.dynamic_values.append(.{ .key = .{ .Const = "SkillIndex" }, .value = 0 });
|
||||||
|
try battle.buff_list.append(buff_firefly);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buffedAvatarId == 8007) {
|
||||||
|
var buff_rmc = BattleBuff{
|
||||||
|
.id = 800701, //for rmc tech
|
||||||
|
.level = 1,
|
||||||
|
.owner_index = @intCast(idx),
|
||||||
|
.wave_flag = 1,
|
||||||
|
.target_index_list = targetIndexList,
|
||||||
|
.dynamic_values = ArrayList(protocol.BattleBuff.DynamicValuesEntry).init(self.allocator),
|
||||||
|
};
|
||||||
|
try buff_rmc.dynamic_values.append(.{ .key = .{ .Const = "SkillIndex" }, .value = 0 });
|
||||||
|
try battle.buff_list.append(buff_rmc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try battle.pve_avatar_list.append(avatar);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Basic battle info
|
||||||
|
battle.battle_id = config.battle_config.battle_id;
|
||||||
|
battle.stage_id = config.battle_config.stage_id;
|
||||||
|
battle.logic_random_seed = @intCast(@mod(std.time.timestamp(), 0xFFFFFFFF));
|
||||||
|
battle.rounds_limit = config.battle_config.cycle_count;
|
||||||
|
battle.monster_wave_length = @intCast(config.battle_config.monster_wave.items.len);
|
||||||
|
battle.world_level = 6;
|
||||||
|
|
||||||
|
// Monster handler
|
||||||
|
for (config.battle_config.monster_wave.items) |wave| {
|
||||||
|
var monster_wave = protocol.SceneMonsterWave.init(self.allocator);
|
||||||
|
monster_wave.wave_param = protocol.SceneMonsterWaveParam{ .level = config.battle_config.monster_level };
|
||||||
|
for (wave.items) |mob_id| {
|
||||||
|
try monster_wave.monster_list.append(.{ .monster_id = mob_id });
|
||||||
|
}
|
||||||
|
try battle.monster_wave_list.append(monster_wave);
|
||||||
|
}
|
||||||
|
// stage blessings
|
||||||
|
for (config.battle_config.blessings.items) |blessing| {
|
||||||
|
var targetIndexList = ArrayList(u32).init(self.allocator);
|
||||||
|
try targetIndexList.append(0);
|
||||||
|
var buff = protocol.BattleBuff{
|
||||||
|
.id = blessing,
|
||||||
|
.level = 1,
|
||||||
|
.owner_index = 0xffffffff,
|
||||||
|
.wave_flag = 0xffffffff,
|
||||||
|
.target_index_list = targetIndexList,
|
||||||
|
.dynamic_values = ArrayList(protocol.BattleBuff.DynamicValuesEntry).init(self.allocator),
|
||||||
|
};
|
||||||
|
try buff.dynamic_values.append(.{ .key = .{ .Const = "SkillIndex" }, .value = 0 });
|
||||||
|
try battle.buff_list.append(buff);
|
||||||
|
}
|
||||||
|
if (isInList(1407, Data.AllAvatars)) { //support Castorice
|
||||||
|
var targetIndexList = ArrayList(u32).init(self.allocator);
|
||||||
|
try targetIndexList.append(0);
|
||||||
|
var mazebuff_data = BattleBuff{
|
||||||
|
.id = 140703,
|
||||||
|
.level = 1,
|
||||||
|
.owner_index = 1,
|
||||||
|
.wave_flag = @intCast(config.battle_config.monster_wave.items.len),
|
||||||
|
.target_index_list = targetIndexList,
|
||||||
|
.dynamic_values = ArrayList(protocol.BattleBuff.DynamicValuesEntry).init(self.allocator),
|
||||||
|
};
|
||||||
|
try mazebuff_data.dynamic_values.append(.{ .key = .{ .Const = "SkillIndex" }, .value = 0 });
|
||||||
|
try battle.buff_list.append(mazebuff_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
// PF/AS scoring
|
||||||
|
const BattleTargetInfoEntry = protocol.SceneBattleInfo.BattleTargetInfoEntry;
|
||||||
|
battle.battle_target_info = ArrayList(BattleTargetInfoEntry).init(self.allocator);
|
||||||
|
|
||||||
|
// target hardcode
|
||||||
|
var pfTargetHead = protocol.BattleTargetList{ .battle_target_list = ArrayList(protocol.BattleTarget).init(self.allocator) };
|
||||||
|
try pfTargetHead.battle_target_list.append(.{ .id = 10002, .progress = 0, .total_progress = 0 });
|
||||||
|
var pfTargetTail = protocol.BattleTargetList{ .battle_target_list = ArrayList(protocol.BattleTarget).init(self.allocator) };
|
||||||
|
try pfTargetTail.battle_target_list.append(.{ .id = 2001, .progress = 0, .total_progress = 0 });
|
||||||
|
try pfTargetTail.battle_target_list.append(.{ .id = 2002, .progress = 0, .total_progress = 0 });
|
||||||
|
var asTargetHead = protocol.BattleTargetList{ .battle_target_list = ArrayList(protocol.BattleTarget).init(self.allocator) };
|
||||||
|
try asTargetHead.battle_target_list.append(.{ .id = 90005, .progress = 0, .total_progress = 0 });
|
||||||
|
|
||||||
|
switch (battle.stage_id) {
|
||||||
|
// PF
|
||||||
|
30019000...30019100, 30021000...30021100, 30301000...30319000 => {
|
||||||
|
try battle.battle_target_info.append(.{ .key = 1, .value = pfTargetHead });
|
||||||
|
// fill blank target
|
||||||
|
for (2..5) |i| {
|
||||||
|
try battle.battle_target_info.append(.{ .key = @intCast(i) });
|
||||||
|
}
|
||||||
|
try battle.battle_target_info.append(.{ .key = 5, .value = pfTargetTail });
|
||||||
|
},
|
||||||
|
// AS
|
||||||
|
420100...420300 => {
|
||||||
|
try battle.battle_target_info.append(.{ .key = 1, .value = asTargetHead });
|
||||||
|
},
|
||||||
|
else => {},
|
||||||
|
}
|
||||||
|
return battle;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
pub fn relicCoder(allocator: Allocator, id: u32, level: u32, main_affix_id: u32, stat1: u32, cnt1: u32, step1: u32, stat2: u32, cnt2: u32, step2: u32, stat3: u32, cnt3: u32, step3: u32, stat4: u32, cnt4: u32, step4: u32) !protocol.BattleRelic {
|
||||||
|
var relic = protocol.BattleRelic{
|
||||||
|
.id = id,
|
||||||
|
.main_affix_id = main_affix_id,
|
||||||
|
.level = level,
|
||||||
|
.sub_affix_list = ArrayList(protocol.RelicAffix).init(allocator),
|
||||||
|
};
|
||||||
|
try relic.sub_affix_list.append(protocol.RelicAffix{ .affix_id = stat1, .cnt = cnt1, .step = step1 });
|
||||||
|
try relic.sub_affix_list.append(protocol.RelicAffix{ .affix_id = stat2, .cnt = cnt2, .step = step2 });
|
||||||
|
try relic.sub_affix_list.append(protocol.RelicAffix{ .affix_id = stat3, .cnt = cnt3, .step = step3 });
|
||||||
|
try relic.sub_affix_list.append(protocol.RelicAffix{ .affix_id = stat4, .cnt = cnt4, .step = step4 });
|
||||||
|
|
||||||
|
return relic;
|
||||||
|
}
|
39
gameserver/src/manager/lineup_mgr.zig
Normal file
39
gameserver/src/manager/lineup_mgr.zig
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const protocol = @import("protocol");
|
||||||
|
const Session = @import("../Session.zig");
|
||||||
|
const Packet = @import("../Packet.zig");
|
||||||
|
const Config = @import("../services/config.zig");
|
||||||
|
const Data = @import("../data.zig");
|
||||||
|
|
||||||
|
const UidGenerator = @import("../services/item.zig").UidGenerator;
|
||||||
|
const ArrayList = std.ArrayList;
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
const CmdID = protocol.CmdID;
|
||||||
|
|
||||||
|
pub const LineupManager = struct {
|
||||||
|
allocator: std.mem.Allocator,
|
||||||
|
pub fn init(allocator: std.mem.Allocator) LineupManager {
|
||||||
|
return LineupManager{ .allocator = allocator };
|
||||||
|
}
|
||||||
|
pub fn createLineup(self: *LineupManager) !protocol.LineupInfo {
|
||||||
|
const config = try Config.loadGameConfig(self.allocator, "config.json");
|
||||||
|
|
||||||
|
var lineup = protocol.LineupInfo.init(self.allocator);
|
||||||
|
lineup.mp = 5;
|
||||||
|
lineup.max_mp = 5;
|
||||||
|
lineup.name = .{ .Const = "CastoriceSR" };
|
||||||
|
|
||||||
|
for (config.avatar_config.items, 0..) |avatarConf, idx| {
|
||||||
|
var avatar = protocol.LineupAvatar.init(self.allocator);
|
||||||
|
avatar.id = avatarConf.id;
|
||||||
|
avatar.slot = @intCast(idx);
|
||||||
|
avatar.satiety = 0;
|
||||||
|
avatar.hp = avatarConf.hp * 100;
|
||||||
|
avatar.sp_bar = .{ .sp_cur = avatarConf.sp * 100, .sp_max = 10000 };
|
||||||
|
avatar.avatar_type = protocol.AvatarType.AVATAR_FORMAL_TYPE;
|
||||||
|
try lineup.avatar_list.append(avatar);
|
||||||
|
}
|
||||||
|
|
||||||
|
return lineup;
|
||||||
|
}
|
||||||
|
};
|
110
gameserver/src/manager/multipath_mgr.zig
Normal file
110
gameserver/src/manager/multipath_mgr.zig
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const protocol = @import("protocol");
|
||||||
|
const Session = @import("../Session.zig");
|
||||||
|
const Packet = @import("../Packet.zig");
|
||||||
|
const Config = @import("../services/config.zig");
|
||||||
|
const Data = @import("../data.zig");
|
||||||
|
|
||||||
|
const ArrayList = std.ArrayList;
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
const CmdID = protocol.CmdID;
|
||||||
|
|
||||||
|
pub const MultiPathManager = struct {
|
||||||
|
allocator: std.mem.Allocator,
|
||||||
|
pub fn init(allocator: std.mem.Allocator) MultiPathManager {
|
||||||
|
return MultiPathManager{ .allocator = allocator };
|
||||||
|
}
|
||||||
|
pub fn createMultiPath(self: *MultiPathManager, skinID: u32) !protocol.GetMultiPathAvatarInfoScRsp {
|
||||||
|
var rsp = protocol.GetMultiPathAvatarInfoScRsp.init(self.allocator);
|
||||||
|
const config = try Config.loadGameConfig(self.allocator, "config.json");
|
||||||
|
const GeneratorType = UidGenerator();
|
||||||
|
const avatar_ids = [_][]const u32{
|
||||||
|
&[_]u32{ 8001, 8002 },
|
||||||
|
&[_]u32{ 8003, 8004 },
|
||||||
|
&[_]u32{ 8005, 8006 },
|
||||||
|
&[_]u32{ 8007, 8008 },
|
||||||
|
&[_]u32{1001},
|
||||||
|
&[_]u32{1224},
|
||||||
|
};
|
||||||
|
const avatar_types = [_]protocol.MultiPathAvatarType{
|
||||||
|
.GirlWarriorType, .GirlKnightType, .GirlShamanType,
|
||||||
|
.GirlMemoryType, .Mar_7thKnightType, .Mar_7thRogueType,
|
||||||
|
};
|
||||||
|
var indexes: [6]u32 = [_]u32{0} ** 6;
|
||||||
|
var counts: [6]u32 = [_]u32{0} ** 6;
|
||||||
|
var multis: [6]protocol.MultiPathAvatarInfo = undefined;
|
||||||
|
for (&multis, avatar_types, 0..) |*multi, avatar_type, i| {
|
||||||
|
std.debug.print("MULTIPATH AVATAR INDEX: {} IS {}\n", .{ i, avatar_type });
|
||||||
|
multi.* = protocol.MultiPathAvatarInfo.init(self.allocator);
|
||||||
|
multi.avatar_id = avatar_type;
|
||||||
|
if (avatar_type == .Mar_7thKnightType) {
|
||||||
|
multi.dressed_skin_id = skinID;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (config.avatar_config.items) |avatar| {
|
||||||
|
for (0..avatar_ids.len) |i| {
|
||||||
|
counts[i] += 1;
|
||||||
|
for (avatar_ids[i]) |id| {
|
||||||
|
if (avatar.id == id) {
|
||||||
|
multis[i].rank = avatar.rank;
|
||||||
|
indexes[i] = counts[i] - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var generators: [6]GeneratorType = undefined;
|
||||||
|
for (0..multis.len) |i| {
|
||||||
|
generators[i] = GeneratorType.init(indexes[i] * 7 + 1);
|
||||||
|
}
|
||||||
|
for (0..multis.len) |i| {
|
||||||
|
var multi = &multis[i];
|
||||||
|
var gen = &generators[i];
|
||||||
|
|
||||||
|
multi.path_equipment_id = indexes[i] * 7 + 1;
|
||||||
|
multi.equip_relic_list = ArrayList(protocol.EquipRelic).init(self.allocator);
|
||||||
|
|
||||||
|
for (0..6) |slot| {
|
||||||
|
try multi.equip_relic_list.append(.{
|
||||||
|
.relic_unique_id = gen.nextId(),
|
||||||
|
.type = @intCast(slot),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (0..multis.len) |i| {
|
||||||
|
const skill_set = if (i == 3) &Data.skills else &Data.skills_old;
|
||||||
|
for (skill_set) |skill| {
|
||||||
|
const talent_level: u32 = if (skill == 1 or skill == 301 or skill == 302) 6 else if (skill <= 4) 10 else 1;
|
||||||
|
const point_id = if (avatar_ids[i].len > 1)
|
||||||
|
avatar_ids[i][1] * 1000 + skill
|
||||||
|
else
|
||||||
|
avatar_ids[i][0] * 1000 + skill;
|
||||||
|
const talent = protocol.AvatarSkillTree{
|
||||||
|
.point_id = point_id,
|
||||||
|
.level = talent_level,
|
||||||
|
};
|
||||||
|
try multis[i].multi_path_skill_tree.append(talent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try rsp.multi_path_avatar_info_list.appendSlice(&multis);
|
||||||
|
try rsp.basic_type_id_list.appendSlice(&Data.MultiAvatar);
|
||||||
|
try rsp.cur_multi_path_avatar_type_map.append(.{ .key = 1001, .value = .Mar_7thKnightType });
|
||||||
|
try rsp.cur_multi_path_avatar_type_map.append(.{ .key = 8001, .value = .GirlMemoryType });
|
||||||
|
rsp.retcode = 0;
|
||||||
|
|
||||||
|
return rsp;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn UidGenerator() type {
|
||||||
|
return struct {
|
||||||
|
current_id: u32,
|
||||||
|
const Self = @This();
|
||||||
|
pub fn init(initial_id: u32) Self {
|
||||||
|
return Self{ .current_id = initial_id };
|
||||||
|
}
|
||||||
|
pub fn nextId(self: *Self) u32 {
|
||||||
|
self.current_id +%= 1;
|
||||||
|
return self.current_id;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
265
gameserver/src/manager/scene_mgr.zig
Normal file
265
gameserver/src/manager/scene_mgr.zig
Normal file
|
@ -0,0 +1,265 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const protocol = @import("protocol");
|
||||||
|
const Session = @import("../Session.zig");
|
||||||
|
const Packet = @import("../Packet.zig");
|
||||||
|
const Config = @import("../services/config.zig");
|
||||||
|
const Res_config = @import("../services/res_config.zig");
|
||||||
|
const Data = @import("../data.zig");
|
||||||
|
|
||||||
|
const UidGenerator = @import("../services/item.zig").UidGenerator;
|
||||||
|
const ArrayList = std.ArrayList;
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
const CmdID = protocol.CmdID;
|
||||||
|
|
||||||
|
pub const SceneManager = struct {
|
||||||
|
allocator: std.mem.Allocator,
|
||||||
|
|
||||||
|
pub fn init(allocator: std.mem.Allocator) SceneManager {
|
||||||
|
return SceneManager{ .allocator = allocator };
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn createScene(
|
||||||
|
self: *SceneManager,
|
||||||
|
plane_id: u32,
|
||||||
|
floor_id: u32,
|
||||||
|
entry_id: u32,
|
||||||
|
teleport_id: u32,
|
||||||
|
) !protocol.SceneInfo {
|
||||||
|
const config = try Config.loadGameConfig(self.allocator, "config.json");
|
||||||
|
const res_config = try Res_config.anchorLoader(self.allocator, "resources/res.json");
|
||||||
|
var generator = UidGenerator().init();
|
||||||
|
var scene_info = protocol.SceneInfo.init(self.allocator);
|
||||||
|
scene_info.plane_id = plane_id;
|
||||||
|
scene_info.floor_id = floor_id;
|
||||||
|
scene_info.entry_id = entry_id;
|
||||||
|
scene_info.leader_entity_id = 100000;
|
||||||
|
scene_info.world_id = 501;
|
||||||
|
scene_info.client_pos_version = 1;
|
||||||
|
var group_map = std.AutoHashMap(u32, protocol.SceneEntityGroupInfo).init(self.allocator);
|
||||||
|
defer group_map.deinit();
|
||||||
|
for (res_config.scene_config.items) |sceneConf| {
|
||||||
|
for (sceneConf.teleports.items) |teleConf| {
|
||||||
|
if (teleConf.teleportId == teleport_id) {
|
||||||
|
var scene_group = protocol.SceneEntityGroupInfo.init(self.allocator);
|
||||||
|
scene_group.state = 1;
|
||||||
|
for (config.avatar_config.items, 0..) |avatarConf, idx| {
|
||||||
|
try scene_group.entity_list.append(.{
|
||||||
|
.InstId = 1,
|
||||||
|
.EntityId = @intCast(idx + 100000),
|
||||||
|
.entityCase_ = .{ .Actor = .{
|
||||||
|
.base_avatar_id = avatarConf.id,
|
||||||
|
.avatar_type = .AVATAR_FORMAL_TYPE,
|
||||||
|
.uid = 0,
|
||||||
|
.map_layer = 0,
|
||||||
|
} },
|
||||||
|
.Motion = .{
|
||||||
|
.pos = .{ .x = teleConf.pos.x, .y = teleConf.pos.y, .z = teleConf.pos.z },
|
||||||
|
.rot = .{ .x = teleConf.rot.x, .y = teleConf.rot.y, .z = teleConf.rot.z },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
try scene_info.entity_group_list.append(scene_group);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (scene_info.plane_id != 10000 and scene_info.plane_id != 10202 and sceneConf.planeID == scene_info.plane_id) {
|
||||||
|
for (sceneConf.props.items) |propConf| {
|
||||||
|
var scene_group = try getOrCreateGroup(&group_map, propConf.groupId, self.allocator);
|
||||||
|
var prop_info = protocol.ScenePropInfo.init(self.allocator);
|
||||||
|
prop_info.prop_id = propConf.propId;
|
||||||
|
prop_info.prop_state = propConf.propState;
|
||||||
|
try scene_group.entity_list.append(.{
|
||||||
|
.entityCase_ = .{ .Prop = prop_info },
|
||||||
|
.GroupId = scene_group.group_id,
|
||||||
|
.InstId = propConf.instId,
|
||||||
|
.EntityId = 0,
|
||||||
|
.Motion = .{
|
||||||
|
.pos = .{ .x = propConf.pos.x, .y = propConf.pos.y, .z = propConf.pos.z },
|
||||||
|
.rot = .{ .x = propConf.rot.x, .y = propConf.rot.y, .z = propConf.rot.z },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
for (sceneConf.monsters.items) |monsConf| {
|
||||||
|
var scene_group = try getOrCreateGroup(&group_map, monsConf.groupId, self.allocator);
|
||||||
|
var monster_info = protocol.SceneNpcMonsterInfo.init(self.allocator);
|
||||||
|
monster_info.monster_id = monsConf.monsterId;
|
||||||
|
monster_info.event_id = monsConf.eventId;
|
||||||
|
monster_info.world_level = 6;
|
||||||
|
try scene_group.entity_list.append(.{
|
||||||
|
.entityCase_ = .{ .NpcMonster = monster_info },
|
||||||
|
.GroupId = scene_group.group_id,
|
||||||
|
.InstId = monsConf.instId,
|
||||||
|
.EntityId = generator.nextId(),
|
||||||
|
.Motion = .{
|
||||||
|
.pos = .{ .x = monsConf.pos.x, .y = monsConf.pos.y, .z = monsConf.pos.z },
|
||||||
|
.rot = .{ .x = monsConf.rot.x, .y = monsConf.rot.y, .z = monsConf.rot.z },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var iter = group_map.iterator();
|
||||||
|
while (iter.next()) |entry| {
|
||||||
|
try scene_info.entity_group_list.append(entry.value_ptr.*);
|
||||||
|
}
|
||||||
|
return scene_info;
|
||||||
|
}
|
||||||
|
fn getOrCreateGroup(group_map: *std.AutoHashMap(u32, protocol.SceneEntityGroupInfo), group_id: u32, allocator: std.mem.Allocator) !*protocol.SceneEntityGroupInfo {
|
||||||
|
if (group_map.getPtr(group_id)) |existing_group| {
|
||||||
|
return existing_group;
|
||||||
|
}
|
||||||
|
var new_group = protocol.SceneEntityGroupInfo.init(allocator);
|
||||||
|
new_group.state = 1;
|
||||||
|
new_group.group_id = group_id;
|
||||||
|
try group_map.put(group_id, new_group);
|
||||||
|
return group_map.getPtr(group_id).?;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
pub const ChallengeSceneManager = struct {
|
||||||
|
allocator: std.mem.Allocator,
|
||||||
|
pub fn init(allocator: std.mem.Allocator) ChallengeSceneManager {
|
||||||
|
return ChallengeSceneManager{ .allocator = allocator };
|
||||||
|
}
|
||||||
|
pub fn createScene(
|
||||||
|
self: *ChallengeSceneManager,
|
||||||
|
avatar_list: ArrayList(u32),
|
||||||
|
plane_id: u32,
|
||||||
|
floor_id: u32,
|
||||||
|
entry_id: u32,
|
||||||
|
world_id: u32,
|
||||||
|
monster_id: u32,
|
||||||
|
group_id: u32,
|
||||||
|
maze_group_id: u32,
|
||||||
|
) !protocol.SceneInfo {
|
||||||
|
const res_config = try Res_config.anchorLoader(self.allocator, "resources/res.json");
|
||||||
|
var generator = UidGenerator().init();
|
||||||
|
|
||||||
|
var scene_info = protocol.SceneInfo.init(self.allocator);
|
||||||
|
scene_info.game_mode_type = 4;
|
||||||
|
scene_info.plane_id = plane_id;
|
||||||
|
scene_info.floor_id = floor_id;
|
||||||
|
scene_info.entry_id = entry_id;
|
||||||
|
scene_info.leader_entity_id = 1;
|
||||||
|
scene_info.world_id = world_id;
|
||||||
|
try scene_info.group_state_list.append(protocol.SceneGroupState{
|
||||||
|
.group_id = maze_group_id,
|
||||||
|
.is_default = true,
|
||||||
|
});
|
||||||
|
{ // Character
|
||||||
|
var scene_group = protocol.SceneEntityGroupInfo.init(self.allocator);
|
||||||
|
scene_group.state = 1;
|
||||||
|
scene_group.group_id = 0;
|
||||||
|
for (avatar_list.items, 0..) |avatarConf, idx| {
|
||||||
|
const newidx = idx + 100000;
|
||||||
|
try scene_group.entity_list.append(.{
|
||||||
|
.InstId = 1,
|
||||||
|
.EntityId = @intCast(newidx),
|
||||||
|
.entityCase_ = .{
|
||||||
|
.Actor = .{
|
||||||
|
.base_avatar_id = avatarConf,
|
||||||
|
.avatar_type = .AVATAR_FORMAL_TYPE,
|
||||||
|
.uid = 1,
|
||||||
|
.map_layer = 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
.Motion = .{ .pos = .{}, .rot = .{} },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
try scene_info.entity_group_list.append(scene_group);
|
||||||
|
}
|
||||||
|
for (res_config.scene_config.items) |sceneConf| {
|
||||||
|
if (sceneConf.planeID == scene_info.plane_id) {
|
||||||
|
for (sceneConf.monsters.items) |monsConf| { //create monster
|
||||||
|
var scene_group = protocol.SceneEntityGroupInfo.init(self.allocator);
|
||||||
|
scene_group.state = 1;
|
||||||
|
if (monsConf.groupId == group_id) {
|
||||||
|
scene_group.group_id = group_id;
|
||||||
|
|
||||||
|
var monster_info = protocol.SceneNpcMonsterInfo.init(self.allocator);
|
||||||
|
monster_info.monster_id = monster_id;
|
||||||
|
monster_info.event_id = monsConf.eventId;
|
||||||
|
monster_info.world_level = 6;
|
||||||
|
|
||||||
|
try scene_group.entity_list.append(.{
|
||||||
|
.entityCase_ = .{ .NpcMonster = monster_info },
|
||||||
|
.GroupId = group_id,
|
||||||
|
.InstId = monsConf.instId,
|
||||||
|
.EntityId = generator.nextId(),
|
||||||
|
.Motion = .{
|
||||||
|
.pos = .{ .x = monsConf.pos.x, .y = monsConf.pos.y, .z = monsConf.pos.z },
|
||||||
|
.rot = .{ .x = monsConf.rot.x, .y = monsConf.rot.y, .z = monsConf.rot.z },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
try scene_info.entity_group_list.append(scene_group);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (res_config.scene_config.items) |sceneConf| {
|
||||||
|
if (sceneConf.planeID == scene_info.plane_id) {
|
||||||
|
for (sceneConf.props.items) |propConf| { //create props
|
||||||
|
var scene_group = protocol.SceneEntityGroupInfo.init(self.allocator);
|
||||||
|
scene_group.state = 1;
|
||||||
|
scene_group.group_id = group_id;
|
||||||
|
|
||||||
|
var prop_info = protocol.ScenePropInfo.init(self.allocator);
|
||||||
|
prop_info.prop_id = propConf.propId;
|
||||||
|
prop_info.prop_state = propConf.propState;
|
||||||
|
|
||||||
|
try scene_group.entity_list.append(.{
|
||||||
|
.entityCase_ = .{ .Prop = prop_info },
|
||||||
|
.GroupId = group_id,
|
||||||
|
.InstId = propConf.instId,
|
||||||
|
.EntityId = 0,
|
||||||
|
.Motion = .{
|
||||||
|
.pos = .{ .x = propConf.pos.x, .y = propConf.pos.y, .z = propConf.pos.z },
|
||||||
|
.rot = .{ .x = propConf.rot.x, .y = propConf.rot.y, .z = propConf.rot.z },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
try scene_info.entity_group_list.append(scene_group);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return scene_info;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const MazeMapManager = struct {
|
||||||
|
allocator: std.mem.Allocator,
|
||||||
|
pub fn init(allocator: std.mem.Allocator) MazeMapManager {
|
||||||
|
return MazeMapManager{ .allocator = allocator };
|
||||||
|
}
|
||||||
|
pub fn setMazeMapData(
|
||||||
|
self: *MazeMapManager,
|
||||||
|
map_info: *protocol.SceneMapInfo,
|
||||||
|
floor_id: u32,
|
||||||
|
) !void {
|
||||||
|
const config = try Config.loadMapEntranceConfig(self.allocator, "resources/MapEntrance.json");
|
||||||
|
const res_config = try Res_config.anchorLoader(self.allocator, "resources/res.json");
|
||||||
|
var plane_ids = ArrayList(u32).init(self.allocator);
|
||||||
|
for (config.map_entrance_config.items) |entrConf| {
|
||||||
|
if (entrConf.floor_id == floor_id) {
|
||||||
|
try plane_ids.append(entrConf.plane_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
map_info.maze_group_list = ArrayList(protocol.MazeGroup).init(self.allocator);
|
||||||
|
map_info.maze_prop_list = ArrayList(protocol.MazePropState).init(self.allocator);
|
||||||
|
for (res_config.scene_config.items) |resConf| {
|
||||||
|
for (plane_ids.items) |plane_id| {
|
||||||
|
if (resConf.planeID == plane_id) {
|
||||||
|
for (resConf.props.items) |propConf| {
|
||||||
|
try map_info.maze_group_list.append(protocol.MazeGroup{
|
||||||
|
.NOBKEONAKLE = ArrayList(u32).init(self.allocator),
|
||||||
|
.group_id = propConf.groupId,
|
||||||
|
});
|
||||||
|
try map_info.maze_prop_list.append(protocol.MazePropState{
|
||||||
|
.group_id = propConf.groupId,
|
||||||
|
.config_id = propConf.instId,
|
||||||
|
.state = propConf.propState,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
42
gameserver/src/network.zig
Normal file
42
gameserver/src/network.zig
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const Session = @import("Session.zig");
|
||||||
|
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
|
||||||
|
pub fn listen() !void {
|
||||||
|
const addr = std.net.Address.parseIp4("0.0.0.0", 23301) catch unreachable;
|
||||||
|
var listener = try addr.listen(.{
|
||||||
|
.kernel_backlog = 100,
|
||||||
|
.reuse_address = true,
|
||||||
|
});
|
||||||
|
|
||||||
|
std.log.info("server is listening at {}", .{listener.listen_address});
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
const conn = listener.accept() catch continue;
|
||||||
|
errdefer conn.stream.close();
|
||||||
|
|
||||||
|
const thread = try std.Thread.spawn(.{}, runSession, .{ conn.address, conn.stream });
|
||||||
|
thread.detach();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn runSession(address: std.net.Address, stream: std.net.Stream) !void {
|
||||||
|
std.log.info("new connection from {}", .{address});
|
||||||
|
|
||||||
|
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||||
|
defer if (gpa.deinit() == .leak) std.log.err("memory leaks were detected for session at {}", .{address});
|
||||||
|
|
||||||
|
const allocator = gpa.allocator();
|
||||||
|
|
||||||
|
const session = try allocator.create(Session);
|
||||||
|
session.* = Session.init(address, stream, allocator);
|
||||||
|
|
||||||
|
if (session.*.run()) |_| {
|
||||||
|
std.log.info("client from {} disconnected", .{address});
|
||||||
|
} else |err| {
|
||||||
|
std.log.err("session disconnected with an error: {}", .{err});
|
||||||
|
}
|
||||||
|
|
||||||
|
allocator.destroy(session);
|
||||||
|
}
|
186
gameserver/src/services/avatar.zig
Normal file
186
gameserver/src/services/avatar.zig
Normal file
|
@ -0,0 +1,186 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const protocol = @import("protocol");
|
||||||
|
const Session = @import("../Session.zig");
|
||||||
|
const Packet = @import("../Packet.zig");
|
||||||
|
const Config = @import("config.zig");
|
||||||
|
const Data = @import("../data.zig");
|
||||||
|
|
||||||
|
const UidGenerator = @import("item.zig").UidGenerator;
|
||||||
|
const ArrayList = std.ArrayList;
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
const CmdID = protocol.CmdID;
|
||||||
|
|
||||||
|
pub var m7th: bool = true;
|
||||||
|
pub var mg: bool = true;
|
||||||
|
pub var mac: u32 = 4;
|
||||||
|
|
||||||
|
// function to check the list if true
|
||||||
|
fn isInList(id: u32, list: []const u32) bool {
|
||||||
|
for (list) |item| {
|
||||||
|
if (item == id) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn onGetAvatarData(session: *Session, packet: *const Packet, allocator: Allocator) !void {
|
||||||
|
const config = try Config.loadGameConfig(allocator, "config.json");
|
||||||
|
var generator = UidGenerator().init();
|
||||||
|
const req = try packet.getProto(protocol.GetAvatarDataCsReq, allocator);
|
||||||
|
var rsp = protocol.GetAvatarDataScRsp.init(allocator);
|
||||||
|
rsp.is_get_all = req.is_get_all;
|
||||||
|
for (Data.AllAvatars) |id| {
|
||||||
|
var avatar = protocol.Avatar.init(allocator);
|
||||||
|
avatar.base_avatar_id = id;
|
||||||
|
avatar.level = 80;
|
||||||
|
avatar.promotion = 6;
|
||||||
|
avatar.rank = 6;
|
||||||
|
avatar.has_taken_promotion_reward_list = ArrayList(u32).init(allocator);
|
||||||
|
for (1..6) |i| {
|
||||||
|
try avatar.has_taken_promotion_reward_list.append(@intCast(i));
|
||||||
|
}
|
||||||
|
var talentLevel: u32 = 0;
|
||||||
|
const skill_list: []const u32 = if (isInList(avatar.base_avatar_id, &Data.Rem)) &Data.skills else &Data.skills_old;
|
||||||
|
for (skill_list) |elem| {
|
||||||
|
talentLevel = switch (elem) {
|
||||||
|
1 => 6,
|
||||||
|
2...4 => 10,
|
||||||
|
301, 302 => if (isInList(avatar.base_avatar_id, &Data.Rem)) 6 else 1,
|
||||||
|
else => 1,
|
||||||
|
};
|
||||||
|
const talent = protocol.AvatarSkillTree{ .point_id = avatar.base_avatar_id * 1000 + elem, .level = talentLevel };
|
||||||
|
try avatar.skilltree_list.append(talent);
|
||||||
|
}
|
||||||
|
try rsp.avatar_list.append(avatar);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (config.avatar_config.items) |avatarConf| { // rewrite data of avatar in config
|
||||||
|
var avatar = protocol.Avatar.init(allocator);
|
||||||
|
avatar.base_avatar_id = switch (avatarConf.id) {
|
||||||
|
8001...8008 => 8001,
|
||||||
|
1224 => 1001,
|
||||||
|
else => avatarConf.id,
|
||||||
|
};
|
||||||
|
avatar.level = avatarConf.level;
|
||||||
|
avatar.promotion = avatarConf.promotion;
|
||||||
|
avatar.rank = avatarConf.rank;
|
||||||
|
avatar.equipment_unique_id = generator.nextId();
|
||||||
|
avatar.equip_relic_list = ArrayList(protocol.EquipRelic).init(allocator);
|
||||||
|
for (0..6) |i| {
|
||||||
|
try avatar.equip_relic_list.append(.{
|
||||||
|
.relic_unique_id = generator.nextId(), // uid
|
||||||
|
.type = @intCast(i), // slot
|
||||||
|
});
|
||||||
|
}
|
||||||
|
var talentLevel: u32 = 0;
|
||||||
|
const skill_list: []const u32 = if (isInList(avatar.base_avatar_id, &Data.Rem)) &Data.skills else &Data.skills_old;
|
||||||
|
for (skill_list) |elem| {
|
||||||
|
talentLevel = switch (elem) {
|
||||||
|
1 => 6,
|
||||||
|
2...4 => 10,
|
||||||
|
301, 302 => if (isInList(avatar.base_avatar_id, &Data.Rem)) 6 else 1,
|
||||||
|
else => 1,
|
||||||
|
};
|
||||||
|
const talent = protocol.AvatarSkillTree{ .point_id = avatar.base_avatar_id * 1000 + elem, .level = talentLevel };
|
||||||
|
try avatar.skilltree_list.append(talent);
|
||||||
|
}
|
||||||
|
try rsp.avatar_list.append(avatar);
|
||||||
|
const avatarType: protocol.MultiPathAvatarType = @enumFromInt(avatarConf.id);
|
||||||
|
if (@intFromEnum(avatarType) > 1) {
|
||||||
|
try session.send(CmdID.CmdSetAvatarPathScRsp, protocol.SetAvatarPathScRsp{
|
||||||
|
.retcode = 0,
|
||||||
|
.avatar_id = avatarType,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try session.send(CmdID.CmdGetAvatarDataScRsp, rsp);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn onGetBasicInfo(session: *Session, _: *const Packet, allocator: Allocator) !void {
|
||||||
|
var rsp = protocol.GetBasicInfoScRsp.init(allocator);
|
||||||
|
rsp.Gender = 2;
|
||||||
|
rsp.IsGenderSet = true;
|
||||||
|
rsp.PlayerSettingInfo = .{};
|
||||||
|
try session.send(CmdID.CmdGetBasicInfoScRsp, rsp);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn onSetAvatarPath(session: *Session, packet: *const Packet, allocator: Allocator) !void {
|
||||||
|
var rsp = protocol.SetAvatarPathScRsp.init(allocator);
|
||||||
|
|
||||||
|
const req = try packet.getProto(protocol.SetAvatarPathCsReq, allocator);
|
||||||
|
rsp.avatar_id = req.avatar_id;
|
||||||
|
if (rsp.avatar_id == protocol.MultiPathAvatarType.Mar_7thKnightType) {
|
||||||
|
m7th = false;
|
||||||
|
} else if (rsp.avatar_id == protocol.MultiPathAvatarType.Mar_7thRogueType) {
|
||||||
|
m7th = true;
|
||||||
|
} else if (rsp.avatar_id == protocol.MultiPathAvatarType.BoyWarriorType) {
|
||||||
|
mac = 1;
|
||||||
|
mg = false;
|
||||||
|
} else if (rsp.avatar_id == protocol.MultiPathAvatarType.BoyKnightType) {
|
||||||
|
mac = 2;
|
||||||
|
mg = false;
|
||||||
|
} else if (rsp.avatar_id == protocol.MultiPathAvatarType.BoyShamanType) {
|
||||||
|
mac = 3;
|
||||||
|
mg = false;
|
||||||
|
} else if (rsp.avatar_id == protocol.MultiPathAvatarType.BoyMemoryType) {
|
||||||
|
mac = 4;
|
||||||
|
mg = false;
|
||||||
|
} else if (rsp.avatar_id == protocol.MultiPathAvatarType.GirlWarriorType) {
|
||||||
|
mac = 1;
|
||||||
|
mg = true;
|
||||||
|
} else if (rsp.avatar_id == protocol.MultiPathAvatarType.GirlKnightType) {
|
||||||
|
mac = 2;
|
||||||
|
mg = true;
|
||||||
|
} else if (rsp.avatar_id == protocol.MultiPathAvatarType.GirlShamanType) {
|
||||||
|
mac = 3;
|
||||||
|
mg = true;
|
||||||
|
} else if (rsp.avatar_id == protocol.MultiPathAvatarType.GirlMemoryType) {
|
||||||
|
mac = 4;
|
||||||
|
mg = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var sync = protocol.AvatarPathChangedNotify.init(allocator);
|
||||||
|
|
||||||
|
if (req.avatar_id == protocol.MultiPathAvatarType.GirlMemoryType) {
|
||||||
|
sync.base_avatar_id = 8008;
|
||||||
|
} else if (req.avatar_id == protocol.MultiPathAvatarType.GirlShamanType) {
|
||||||
|
sync.base_avatar_id = 8006;
|
||||||
|
} else if (req.avatar_id == protocol.MultiPathAvatarType.GirlKnightType) {
|
||||||
|
sync.base_avatar_id = 8004;
|
||||||
|
} else if (req.avatar_id == protocol.MultiPathAvatarType.GirlWarriorType) {
|
||||||
|
sync.base_avatar_id = 8002;
|
||||||
|
}
|
||||||
|
sync.cur_multi_path_avatar_type = req.avatar_id;
|
||||||
|
|
||||||
|
try session.send(CmdID.CmdAvatarPathChangedNotify, sync);
|
||||||
|
try session.send(CmdID.CmdSetAvatarPathScRsp, rsp);
|
||||||
|
}
|
||||||
|
pub fn onDressAvatarSkin(session: *Session, _: *const Packet, allocator: Allocator) !void {
|
||||||
|
var rsp = protocol.DressAvatarSkinScRsp.init(allocator);
|
||||||
|
rsp.retcode = 0;
|
||||||
|
try session.send(CmdID.CmdDressAvatarSkinScRsp, rsp);
|
||||||
|
}
|
||||||
|
pub fn onTakeOffAvatarSkin(session: *Session, _: *const Packet, allocator: Allocator) !void {
|
||||||
|
var rsp = protocol.TakeOffAvatarSkinScRsp.init(allocator);
|
||||||
|
rsp.retcode = 0;
|
||||||
|
try session.send(CmdID.CmdTakeOffAvatarSkinScRsp, rsp);
|
||||||
|
}
|
||||||
|
pub fn onGetBigDataAll(session: *Session, packet: *const Packet, allocator: Allocator) !void {
|
||||||
|
const req = try packet.getProto(protocol.GetBigDataAllRecommendCsReq, allocator);
|
||||||
|
var rsp = protocol.GetBigDataAllRecommendScRsp.init(allocator);
|
||||||
|
rsp.retcode = 0;
|
||||||
|
rsp.IANNEEIJAKH = req.IANNEEIJAKH;
|
||||||
|
std.debug.print("PRINT BIG DATA ALL REQ {}\n", .{req});
|
||||||
|
try session.send(CmdID.CmdGetBigDataAllRecommendScRsp, rsp);
|
||||||
|
}
|
||||||
|
pub fn onGetBigData(session: *Session, packet: *const Packet, allocator: Allocator) !void {
|
||||||
|
const req = try packet.getProto(protocol.GetBigDataRecommendCsReq, allocator);
|
||||||
|
var rsp = protocol.GetBigDataRecommendScRsp.init(allocator);
|
||||||
|
rsp.retcode = 0;
|
||||||
|
rsp.IANNEEIJAKH = req.IANNEEIJAKH;
|
||||||
|
rsp.EIGPMIBCIKG = req.EIGPMIBCIKG;
|
||||||
|
std.debug.print("PRINT BIG DATA REQ {}\n", .{req});
|
||||||
|
try session.send(CmdID.CmdGetBigDataRecommendScRsp, rsp);
|
||||||
|
}
|
91
gameserver/src/services/battle.zig
Normal file
91
gameserver/src/services/battle.zig
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const protocol = @import("protocol");
|
||||||
|
const Session = @import("../Session.zig");
|
||||||
|
const Packet = @import("../Packet.zig");
|
||||||
|
const Config = @import("config.zig");
|
||||||
|
const Data = @import("../data.zig");
|
||||||
|
const BattleManager = @import("../manager/battle_mgr.zig").BattleManager;
|
||||||
|
|
||||||
|
const ArrayList = std.ArrayList;
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
const CmdID = protocol.CmdID;
|
||||||
|
|
||||||
|
pub fn onStartCocoonStage(session: *Session, packet: *const Packet, allocator: Allocator) !void {
|
||||||
|
const req = try packet.getProto(protocol.StartCocoonStageCsReq, allocator);
|
||||||
|
var battle_mgr = BattleManager.init(allocator);
|
||||||
|
const battle = try battle_mgr.createBattle();
|
||||||
|
try session.send(CmdID.CmdStartCocoonStageScRsp, protocol.StartCocoonStageScRsp{
|
||||||
|
.retcode = 0,
|
||||||
|
.cocoon_id = req.cocoon_id,
|
||||||
|
.prop_entity_id = req.prop_entity_id,
|
||||||
|
.wave = req.wave,
|
||||||
|
.battle_info = battle,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
pub fn onQuickStartCocoonStage(session: *Session, packet: *const Packet, allocator: Allocator) !void {
|
||||||
|
const req = try packet.getProto(protocol.QuickStartCocoonStageCsReq, allocator);
|
||||||
|
var battle_mgr = BattleManager.init(allocator);
|
||||||
|
const battle = try battle_mgr.createBattle();
|
||||||
|
try session.send(CmdID.CmdQuickStartCocoonStageScRsp, protocol.QuickStartCocoonStageScRsp{
|
||||||
|
.retcode = 0,
|
||||||
|
.cocoon_id = req.cocoon_id,
|
||||||
|
.wave = req.wave,
|
||||||
|
.battle_info = battle,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
pub fn onQuickStartFarmElement(session: *Session, packet: *const Packet, allocator: Allocator) !void {
|
||||||
|
const req = try packet.getProto(protocol.QuickStartFarmElementCsReq, allocator);
|
||||||
|
var battle_mgr = BattleManager.init(allocator);
|
||||||
|
const battle = try battle_mgr.createBattle();
|
||||||
|
try session.send(CmdID.CmdQuickStartFarmElementScRsp, protocol.QuickStartFarmElementScRsp{
|
||||||
|
.retcode = 0,
|
||||||
|
.world_level = req.world_level,
|
||||||
|
.JDANOKNHNHL = req.JDANOKNHNHL,
|
||||||
|
.battle_info = battle,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
pub fn onStartBattleCollege(session: *Session, packet: *const Packet, allocator: Allocator) !void {
|
||||||
|
const req = try packet.getProto(protocol.StartBattleCollegeCsReq, allocator);
|
||||||
|
var battle_mgr = BattleManager.init(allocator);
|
||||||
|
const battle = try battle_mgr.createBattle();
|
||||||
|
try session.send(CmdID.CmdStartBattleCollegeScRsp, protocol.StartBattleCollegeScRsp{
|
||||||
|
.retcode = 0,
|
||||||
|
.id = req.id,
|
||||||
|
.battle_info = battle,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
pub fn onSceneCastSkill(session: *Session, packet: *const Packet, allocator: Allocator) !void {
|
||||||
|
var battle_mgr = BattleManager.init(allocator);
|
||||||
|
const battle = try battle_mgr.createBattle();
|
||||||
|
|
||||||
|
const req = try packet.getProto(protocol.SceneCastSkillCsReq, allocator);
|
||||||
|
var monster_battle_info_list = ArrayList(protocol.HitMonsterBattleInfo).init(allocator);
|
||||||
|
try monster_battle_info_list.appendSlice(&[_]protocol.HitMonsterBattleInfo{
|
||||||
|
.{
|
||||||
|
.target_monster_entity_id = 0,
|
||||||
|
.monster_battle_type = protocol.MonsterBattleType.MONSTER_BATTLE_TYPE_TRIGGER_BATTLE,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
try session.send(CmdID.CmdSceneCastSkillScRsp, protocol.SceneCastSkillScRsp{
|
||||||
|
.retcode = 0,
|
||||||
|
.cast_entity_id = req.cast_entity_id,
|
||||||
|
.monster_battle_info = monster_battle_info_list,
|
||||||
|
.battle_info = if (req.assist_monster_entity_id_list.items.len > 0 or (req.attacked_by_entity_id >= 1 and req.attacked_by_entity_id <= 99)) battle else null,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn onPVEBattleResult(session: *Session, packet: *const Packet, allocator: Allocator) !void {
|
||||||
|
const req = try packet.getProto(protocol.PVEBattleResultCsReq, allocator);
|
||||||
|
var rsp = protocol.PVEBattleResultScRsp.init(allocator);
|
||||||
|
rsp.battle_id = req.battle_id;
|
||||||
|
rsp.end_status = req.end_status;
|
||||||
|
try session.send(CmdID.CmdPVEBattleResultScRsp, rsp);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn onSceneCastSkillCostMp(session: *Session, packet: *const Packet, allocator: Allocator) !void {
|
||||||
|
const req = try packet.getProto(protocol.SceneCastSkillCostMpCsReq, allocator);
|
||||||
|
try session.send(CmdID.CmdSceneCastSkillCostMpScRsp, protocol.SceneCastSkillCostMpScRsp{
|
||||||
|
.retcode = 0,
|
||||||
|
.cast_entity_id = req.cast_entity_id,
|
||||||
|
});
|
||||||
|
}
|
207
gameserver/src/services/challenge.zig
Normal file
207
gameserver/src/services/challenge.zig
Normal file
|
@ -0,0 +1,207 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const protocol = @import("protocol");
|
||||||
|
const Session = @import("../Session.zig");
|
||||||
|
const Packet = @import("../Packet.zig");
|
||||||
|
const Config = @import("config.zig");
|
||||||
|
const Res_config = @import("res_config.zig");
|
||||||
|
const Data = @import("../data.zig");
|
||||||
|
const SceneManager = @import("../manager/scene_mgr.zig").SceneManager;
|
||||||
|
const ChallengeSceneManager = @import("../manager/scene_mgr.zig").ChallengeSceneManager;
|
||||||
|
const LineupManager = @import("../manager/lineup_mgr.zig").LineupManager;
|
||||||
|
const NodeCheck = @import("../commands/value.zig");
|
||||||
|
|
||||||
|
const ArrayList = std.ArrayList;
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
const CmdID = protocol.CmdID;
|
||||||
|
|
||||||
|
pub fn UidGenerator() type {
|
||||||
|
return struct {
|
||||||
|
current_id: u32,
|
||||||
|
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
pub fn init() Self {
|
||||||
|
return Self{ .current_id = 100000 };
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn nextId(self: *Self) u32 {
|
||||||
|
self.current_id +%= 1; // Using wrapping addition
|
||||||
|
return self.current_id;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn contains(list: *const std.ArrayListAligned(u32, null), value: u32) bool {
|
||||||
|
for (list.items) |item| {
|
||||||
|
if (item == value) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn onGetChallenge(session: *Session, _: *const Packet, allocator: Allocator) !void {
|
||||||
|
const challenge_config = try Config.loadChallengeConfig(allocator, "resources/ChallengeMazeConfig.json");
|
||||||
|
var rsp = protocol.GetChallengeScRsp.init(allocator);
|
||||||
|
|
||||||
|
rsp.retcode = 0;
|
||||||
|
for (challenge_config.challenge_config.items) |ids| {
|
||||||
|
var challenge = protocol.Challenge.init(allocator);
|
||||||
|
challenge.challenge_id = ids.id;
|
||||||
|
challenge.star = 1;
|
||||||
|
if (ids.id > 20000 and ids.id < 30000) {
|
||||||
|
challenge.score_id = 40000;
|
||||||
|
challenge.score_two = 40000;
|
||||||
|
}
|
||||||
|
try rsp.challenge_list.append(challenge);
|
||||||
|
}
|
||||||
|
|
||||||
|
try session.send(CmdID.CmdGetChallengeScRsp, rsp);
|
||||||
|
}
|
||||||
|
pub fn onGetChallengeGroupStatistics(session: *Session, packet: *const Packet, allocator: Allocator) !void {
|
||||||
|
const req = try packet.getProto(protocol.GetChallengeGroupStatisticsCsReq, allocator);
|
||||||
|
var rsp = protocol.GetChallengeGroupStatisticsScRsp.init(allocator);
|
||||||
|
rsp.retcode = 0;
|
||||||
|
rsp.group_id = req.group_id;
|
||||||
|
|
||||||
|
try session.send(CmdID.CmdGetChallengeGroupStatisticsScRsp, rsp);
|
||||||
|
}
|
||||||
|
pub fn onLeaveChallenge(session: *Session, _: *const Packet, allocator: Allocator) !void {
|
||||||
|
var lineup_mgr = LineupManager.init(allocator);
|
||||||
|
const lineup = try lineup_mgr.createLineup();
|
||||||
|
var scene_manager = SceneManager.init(allocator);
|
||||||
|
const scene_info = try scene_manager.createScene(20432, 20432001, 2043201, 1213);
|
||||||
|
try session.send(CmdID.CmdQuitBattleScNotify, protocol.QuitBattleScNotify{});
|
||||||
|
try session.send(CmdID.CmdEnterSceneByServerScNotify, protocol.EnterSceneByServerScNotify{
|
||||||
|
.reason = protocol.EnterSceneReason.ENTER_SCENE_REASON_NONE,
|
||||||
|
.lineup = lineup,
|
||||||
|
.scene = scene_info,
|
||||||
|
});
|
||||||
|
try session.send(CmdID.CmdLeaveChallengeScRsp, protocol.LeaveChallengeScRsp{
|
||||||
|
.retcode = 0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
//TODO: IMPLEMENT CHALLENGE BUFF AND FIX MENU
|
||||||
|
pub fn onStartChallenge(session: *Session, packet: *const Packet, allocator: Allocator) !void {
|
||||||
|
const req = try packet.getProto(protocol.StartChallengeCsReq, allocator);
|
||||||
|
var rsp = protocol.StartChallengeScRsp.init(allocator);
|
||||||
|
const challenge_config = try Config.loadChallengeConfig(allocator, "resources/ChallengeMazeConfig.json");
|
||||||
|
const entrance_config = try Config.loadMapEntranceConfig(allocator, "resources/MapEntrance.json");
|
||||||
|
const maze_config = try Config.loadMazePlaneConfig(allocator, "resources/MazePlane.json");
|
||||||
|
|
||||||
|
var avatarList = std.ArrayList(u32).init(allocator);
|
||||||
|
var lineup1 = protocol.LineupInfo.init(allocator);
|
||||||
|
lineup1.mp = 5;
|
||||||
|
lineup1.max_mp = 5;
|
||||||
|
lineup1.extra_lineup_type = protocol.ExtraLineupType.LINEUP_CHALLENGE;
|
||||||
|
for (req.first_lineup.items, 0..) |avt, idx| {
|
||||||
|
var list = protocol.LineupAvatar.init(allocator);
|
||||||
|
try avatarList.append(avt);
|
||||||
|
list.id = avt;
|
||||||
|
list.slot = @intCast(idx);
|
||||||
|
list.satiety = 0;
|
||||||
|
list.avatar_type = protocol.AvatarType.AVATAR_FORMAL_TYPE;
|
||||||
|
list.hp = 10000;
|
||||||
|
list.sp_bar = .{ .sp_cur = 10000, .sp_max = 10000 };
|
||||||
|
try lineup1.avatar_list.append(list);
|
||||||
|
}
|
||||||
|
try rsp.lineup_list.append(lineup1);
|
||||||
|
|
||||||
|
var avatarList2 = std.ArrayList(u32).init(allocator);
|
||||||
|
var lineup2 = protocol.LineupInfo.init(allocator);
|
||||||
|
lineup2.mp = 5;
|
||||||
|
lineup2.max_mp = 5;
|
||||||
|
lineup2.extra_lineup_type = protocol.ExtraLineupType.LINEUP_CHALLENGE_2;
|
||||||
|
for (req.second_lineup.items, 0..) |avt, idx| {
|
||||||
|
var list = protocol.LineupAvatar.init(allocator);
|
||||||
|
try avatarList2.append(avt);
|
||||||
|
list.id = avt;
|
||||||
|
list.slot = @intCast(idx);
|
||||||
|
list.satiety = 0;
|
||||||
|
list.avatar_type = protocol.AvatarType.AVATAR_FORMAL_TYPE;
|
||||||
|
list.hp = 10000;
|
||||||
|
list.sp_bar = .{ .sp_cur = 10000, .sp_max = 10000 };
|
||||||
|
try lineup2.avatar_list.append(list);
|
||||||
|
}
|
||||||
|
try rsp.lineup_list.append(lineup2);
|
||||||
|
|
||||||
|
var cur_challenge_info = protocol.CurChallenge.init(allocator);
|
||||||
|
cur_challenge_info.challenge_id = req.challenge_id;
|
||||||
|
cur_challenge_info.score_id = if (req.challenge_id > 20000 and req.challenge_id < 30000) 40000 else 0;
|
||||||
|
cur_challenge_info.score_two = 0;
|
||||||
|
cur_challenge_info.status = protocol.ChallengeStatus.CHALLENGE_DOING;
|
||||||
|
cur_challenge_info.extra_lineup_type = if (NodeCheck.challenge_node == 0) protocol.ExtraLineupType.LINEUP_CHALLENGE else protocol.ExtraLineupType.LINEUP_CHALLENGE_2;
|
||||||
|
var planeID: u32 = 0;
|
||||||
|
var floorID: u32 = 0;
|
||||||
|
var entryID: u32 = 0;
|
||||||
|
var worldID: u32 = 0;
|
||||||
|
var monsterID: u32 = 0;
|
||||||
|
var groupID: u32 = 0;
|
||||||
|
var maze_groupID: u32 = 0;
|
||||||
|
if (NodeCheck.challenge_node == 0) {
|
||||||
|
for (challenge_config.challenge_config.items) |challengeConf| {
|
||||||
|
if (challengeConf.id == req.challenge_id) {
|
||||||
|
std.debug.print("TRACING CONFIG ID {} WITH CHALLENGE ID {}\n", .{ challengeConf.id, req.challenge_id });
|
||||||
|
for (entrance_config.map_entrance_config.items) |entrance| {
|
||||||
|
if (entrance.id == challengeConf.map_entrance_id) {
|
||||||
|
for (maze_config.maze_plane_config.items) |maze| {
|
||||||
|
if (contains(&maze.floor_id_list, entrance.floor_id)) {
|
||||||
|
floorID = entrance.floor_id;
|
||||||
|
worldID = maze.world_id;
|
||||||
|
monsterID = challengeConf.npc_monster_id_list1.items[challengeConf.npc_monster_id_list1.items.len - 1];
|
||||||
|
groupID = challengeConf.maze_group_id1;
|
||||||
|
maze_groupID = challengeConf.maze_group_id1;
|
||||||
|
planeID = maze.challenge_plane_id;
|
||||||
|
entryID = challengeConf.map_entrance_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (challenge_config.challenge_config.items) |challengeConf| {
|
||||||
|
if (challengeConf.id == req.challenge_id) {
|
||||||
|
std.debug.print("TRACING CONFIG ID {} WITH CHALLENGE ID {}\n", .{ challengeConf.id, req.challenge_id });
|
||||||
|
for (entrance_config.map_entrance_config.items) |entrance| {
|
||||||
|
if (entrance.id == challengeConf.map_entrance_id2) {
|
||||||
|
for (maze_config.maze_plane_config.items) |maze| {
|
||||||
|
if (contains(&maze.floor_id_list, entrance.floor_id)) {
|
||||||
|
floorID = entrance.floor_id;
|
||||||
|
worldID = maze.world_id;
|
||||||
|
monsterID = challengeConf.npc_monster_id_list2.items[challengeConf.npc_monster_id_list2.items.len - 1];
|
||||||
|
groupID = challengeConf.maze_group_id2;
|
||||||
|
maze_groupID = challengeConf.maze_group_id2;
|
||||||
|
planeID = maze.challenge_plane_id;
|
||||||
|
entryID = challengeConf.map_entrance_id2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var scene_PF_manager = ChallengeSceneManager.init(allocator);
|
||||||
|
const scene_info = try scene_PF_manager.createScene(
|
||||||
|
if (NodeCheck.challenge_node == 0) avatarList else avatarList2,
|
||||||
|
planeID,
|
||||||
|
floorID,
|
||||||
|
entryID,
|
||||||
|
worldID,
|
||||||
|
monsterID,
|
||||||
|
groupID,
|
||||||
|
maze_groupID,
|
||||||
|
);
|
||||||
|
rsp.retcode = 0;
|
||||||
|
rsp.scene = scene_info;
|
||||||
|
rsp.cur_challenge = cur_challenge_info;
|
||||||
|
|
||||||
|
try session.send(CmdID.CmdStartChallengeScRsp, rsp);
|
||||||
|
|
||||||
|
std.debug.print("SEND PLANE ID {} FLOOR ID {} ENTRY ID {} GROUP ID {} MAZE GROUP ID {}\n", .{ planeID, floorID, entryID, groupID, maze_groupID });
|
||||||
|
try session.send(CmdID.CmdEnterSceneByServerScNotify, protocol.EnterSceneByServerScNotify{
|
||||||
|
.lineup = if (NodeCheck.challenge_node == 0) lineup1 else lineup2,
|
||||||
|
.reason = protocol.EnterSceneReason.ENTER_SCENE_REASON_DIMENSION_MERGE,
|
||||||
|
.scene = scene_info,
|
||||||
|
});
|
||||||
|
}
|
116
gameserver/src/services/chat.zig
Normal file
116
gameserver/src/services/chat.zig
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const protocol = @import("protocol");
|
||||||
|
const Session = @import("../Session.zig");
|
||||||
|
const Packet = @import("../Packet.zig");
|
||||||
|
const commandhandler = @import("../command.zig");
|
||||||
|
|
||||||
|
const ArrayList = std.ArrayList;
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
const CmdID = protocol.CmdID;
|
||||||
|
|
||||||
|
const B64Decoder = std.base64.standard.Decoder;
|
||||||
|
|
||||||
|
const EmojiList = [_]u32{};
|
||||||
|
|
||||||
|
pub fn onGetFriendListInfo(session: *Session, _: *const Packet, allocator: Allocator) !void {
|
||||||
|
var rsp = protocol.GetFriendListInfoScRsp.init(allocator);
|
||||||
|
rsp.retcode = 0;
|
||||||
|
|
||||||
|
var assist_list = ArrayList(protocol.AssistSimpleInfo).init(allocator);
|
||||||
|
try assist_list.appendSlice(&[_]protocol.AssistSimpleInfo{
|
||||||
|
.{ .Pos = 0, .Level = 80, .AvatarId = 1403, .DressedSkinId = 0 },
|
||||||
|
.{ .Pos = 1, .Level = 80, .AvatarId = 1407, .DressedSkinId = 0 },
|
||||||
|
.{ .Pos = 2, .Level = 80, .AvatarId = 1001, .DressedSkinId = 1100101 },
|
||||||
|
});
|
||||||
|
|
||||||
|
var friend = protocol.FriendSimpleInfo.init(allocator);
|
||||||
|
friend.playing_state = .PLAYING_CHALLENGE_BOSS;
|
||||||
|
friend.create_time = 0; //timestamp
|
||||||
|
friend.remark_name = .{ .Const = "ReversedRooms" }; //friend_custom_nickname
|
||||||
|
friend.is_marked = true;
|
||||||
|
friend.player_simple_info = protocol.PlayerSimpleInfo{
|
||||||
|
.signature = .{ .Const = "https://discord.gg/reversedrooms" },
|
||||||
|
.nickname = .{ .Const = "CipherSR" },
|
||||||
|
.level = 70,
|
||||||
|
.uid = 2000,
|
||||||
|
.head_icon = 200136,
|
||||||
|
.ANPLLAOBFJI = 253000,
|
||||||
|
.chat_bubble_id = 220008,
|
||||||
|
.assist_simple_info = assist_list,
|
||||||
|
.platform_type = protocol.PlatformType.ANDROID,
|
||||||
|
.online_status = protocol.FriendOnlineStatus.FRIEND_ONLINE_STATUS_ONLINE,
|
||||||
|
};
|
||||||
|
try rsp.friend_list.append(friend);
|
||||||
|
try session.send(CmdID.CmdGetFriendListInfoScRsp, rsp);
|
||||||
|
}
|
||||||
|
pub fn onChatEmojiList(session: *Session, _: *const Packet, allocator: Allocator) !void {
|
||||||
|
var rsp = protocol.GetChatEmojiListScRsp.init(allocator);
|
||||||
|
|
||||||
|
rsp.retcode = 0;
|
||||||
|
try rsp.chat_emoji_list.appendSlice(&EmojiList);
|
||||||
|
|
||||||
|
try session.send(CmdID.CmdGetChatEmojiListScRsp, rsp);
|
||||||
|
}
|
||||||
|
pub fn onPrivateChatHistory(session: *Session, _: *const Packet, allocator: Allocator) !void {
|
||||||
|
var rsp = protocol.GetPrivateChatHistoryScRsp.init(allocator);
|
||||||
|
|
||||||
|
rsp.retcode = 0;
|
||||||
|
rsp.target_side = 1;
|
||||||
|
rsp.contact_side = 2000;
|
||||||
|
try rsp.chat_message_list.appendSlice(&[_]protocol.ChatMessageData{
|
||||||
|
.{
|
||||||
|
.content = .{ .Const = "/help for command list" },
|
||||||
|
.message_type = .MSG_TYPE_CUSTOM_TEXT,
|
||||||
|
.create_time = 0,
|
||||||
|
.sender_id = 2000,
|
||||||
|
},
|
||||||
|
.{
|
||||||
|
.content = .{ .Const = "to use command, use '/' first" },
|
||||||
|
.message_type = .MSG_TYPE_CUSTOM_TEXT,
|
||||||
|
.create_time = 0,
|
||||||
|
.sender_id = 2000,
|
||||||
|
},
|
||||||
|
.{
|
||||||
|
.extra_id = 122004,
|
||||||
|
.message_type = .MSG_TYPE_EMOJI,
|
||||||
|
.create_time = 0,
|
||||||
|
.sender_id = 2000,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
try session.send(CmdID.CmdGetPrivateChatHistoryScRsp, rsp);
|
||||||
|
}
|
||||||
|
pub fn onSendMsg(session: *Session, packet: *const Packet, allocator: Allocator) !void {
|
||||||
|
std.debug.print("Received packet: {any}\n", .{packet});
|
||||||
|
const req = protocol.SendMsgCsReq.init(allocator);
|
||||||
|
std.debug.print("Decoded request: {any}\n", .{req});
|
||||||
|
std.debug.print("Raw packet body: {any}\n", .{packet.body});
|
||||||
|
const msg_text = switch (req.message_text) {
|
||||||
|
.Empty => "",
|
||||||
|
.Owned => |owned| owned.str,
|
||||||
|
.Const => |const_str| const_str,
|
||||||
|
};
|
||||||
|
var msg_text2: []const u8 = "";
|
||||||
|
if (packet.body.len > 9 and packet.body[8] == 47) {
|
||||||
|
msg_text2 = packet.body[8 .. packet.body.len - 2];
|
||||||
|
}
|
||||||
|
std.debug.print("Manually extracted message text: '{s}'\n", .{msg_text2});
|
||||||
|
|
||||||
|
std.debug.print("Message Text 1: {any}\n", .{msg_text});
|
||||||
|
|
||||||
|
if (msg_text2.len > 0) {
|
||||||
|
if (std.mem.indexOf(u8, msg_text2, "/") != null) {
|
||||||
|
std.debug.print("Message contains a '/'\n", .{});
|
||||||
|
try commandhandler.handleCommand(session, msg_text2, allocator);
|
||||||
|
} else {
|
||||||
|
std.debug.print("Message does not contain a '/'\n", .{});
|
||||||
|
try commandhandler.sendMessage(session, msg_text2, allocator);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
std.debug.print("Empty message received\n", .{});
|
||||||
|
}
|
||||||
|
|
||||||
|
var rsp = protocol.SendMsgScRsp.init(allocator);
|
||||||
|
rsp.retcode = 0;
|
||||||
|
try session.send(CmdID.CmdSendMsgScRsp, rsp);
|
||||||
|
}
|
338
gameserver/src/services/config.zig
Normal file
338
gameserver/src/services/config.zig
Normal file
|
@ -0,0 +1,338 @@
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
const ArrayList = std.ArrayList;
|
||||||
|
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
const BattleConfig = struct {
|
||||||
|
battle_id: u32,
|
||||||
|
stage_id: u32,
|
||||||
|
cycle_count: u32,
|
||||||
|
monster_wave: ArrayList(ArrayList(u32)),
|
||||||
|
monster_level: u32,
|
||||||
|
blessings: ArrayList(u32),
|
||||||
|
};
|
||||||
|
|
||||||
|
const ExtraMazeBuff = struct {
|
||||||
|
enable: bool,
|
||||||
|
mazebuff: ArrayList(u32),
|
||||||
|
};
|
||||||
|
|
||||||
|
const Lightcone = struct {
|
||||||
|
id: u32,
|
||||||
|
rank: u32,
|
||||||
|
level: u32,
|
||||||
|
promotion: u32,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Relic = struct {
|
||||||
|
id: u32,
|
||||||
|
level: u32,
|
||||||
|
main_affix_id: u32,
|
||||||
|
sub_count: u32,
|
||||||
|
stat1: u32,
|
||||||
|
cnt1: u32,
|
||||||
|
step1: u32,
|
||||||
|
stat2: u32,
|
||||||
|
cnt2: u32,
|
||||||
|
step2: u32,
|
||||||
|
stat3: u32,
|
||||||
|
cnt3: u32,
|
||||||
|
step3: u32,
|
||||||
|
stat4: u32,
|
||||||
|
cnt4: u32,
|
||||||
|
step4: u32,
|
||||||
|
};
|
||||||
|
|
||||||
|
const Avatar = struct {
|
||||||
|
id: u32,
|
||||||
|
hp: u32,
|
||||||
|
sp: u32,
|
||||||
|
level: u32,
|
||||||
|
promotion: u32,
|
||||||
|
rank: u32,
|
||||||
|
lightcone: Lightcone,
|
||||||
|
relics: ArrayList(Relic),
|
||||||
|
use_technique: bool,
|
||||||
|
};
|
||||||
|
|
||||||
|
const ChallengeConfig = struct {
|
||||||
|
id: u32, //
|
||||||
|
npc_monster_id_list1: ArrayList(u32), //
|
||||||
|
npc_monster_id_list2: ArrayList(u32), //
|
||||||
|
event_id_list1: ArrayList(u32), //
|
||||||
|
event_id_list2: ArrayList(u32), //
|
||||||
|
map_entrance_id: u32, //
|
||||||
|
map_entrance_id2: u32, //
|
||||||
|
maze_group_id1: u32, //
|
||||||
|
maze_group_id2: u32, //
|
||||||
|
};
|
||||||
|
|
||||||
|
const MapEntrance = struct {
|
||||||
|
floor_id: u32,
|
||||||
|
id: u32,
|
||||||
|
plane_id: u32,
|
||||||
|
begin_main_mission_idlist: ArrayList(u32),
|
||||||
|
finish_main_mission_idlist: ArrayList(u32),
|
||||||
|
finish_sub_mission_idlist: ArrayList(u32),
|
||||||
|
};
|
||||||
|
const MazePlane = struct {
|
||||||
|
floor_id_list: ArrayList(u32),
|
||||||
|
start_floor_id: u32,
|
||||||
|
challenge_plane_id: u32,
|
||||||
|
world_id: u32,
|
||||||
|
};
|
||||||
|
pub const GameConfig = struct {
|
||||||
|
battle_config: BattleConfig,
|
||||||
|
avatar_config: ArrayList(Avatar),
|
||||||
|
};
|
||||||
|
pub const ChallengeMazeConfig = struct {
|
||||||
|
challenge_config: ArrayList(ChallengeConfig),
|
||||||
|
};
|
||||||
|
pub const MapEntranceConfig = struct {
|
||||||
|
map_entrance_config: ArrayList(MapEntrance),
|
||||||
|
};
|
||||||
|
pub const MazePlaneConfig = struct {
|
||||||
|
maze_plane_config: ArrayList(MazePlane),
|
||||||
|
};
|
||||||
|
const ErrorSet = error{ CommandError, SystemResources, Unexpected, AccessDenied, WouldBlock, ConnectionResetByPeer, OutOfMemory, DiskQuota, FileTooBig, InputOutput, NoSpaceLeft, DeviceBusy, InvalidArgument, BrokenPipe, OperationAborted, NotOpenForWriting, LockViolation, Overflow, InvalidCharacter, ProcessFdQuotaExceeded, SystemFdQuotaExceeded, SymLinkLoop, NameTooLong, FileNotFound, NotDir, NoDevice, SharingViolation, PathAlreadyExists, PipeBusy, InvalidUtf8, InvalidWtf8, BadPathName, NetworkNotFound, AntivirusInterference, IsDir, FileLocksNotSupported, FileBusy, ConnectionTimedOut, NotOpenForReading, SocketNotConnected, Unseekable, UnexpectedToken, InvalidNumber, InvalidEnumTag, DuplicateField, UnknownField, MissingField, LengthMismatch, SyntaxError, UnexpectedEndOfInput, BufferUnderrun, ValueTooLong, InsufficientTokens, InvalidFormat };
|
||||||
|
|
||||||
|
pub fn loadConfig(
|
||||||
|
comptime ConfigType: type,
|
||||||
|
comptime parseFn: fn (std.json.Value, Allocator) ErrorSet!ConfigType,
|
||||||
|
allocator: Allocator,
|
||||||
|
filename: []const u8,
|
||||||
|
) ErrorSet!ConfigType {
|
||||||
|
const file = try std.fs.cwd().openFile(filename, .{});
|
||||||
|
defer file.close();
|
||||||
|
|
||||||
|
const file_size = try file.getEndPos();
|
||||||
|
const buffer = try file.readToEndAlloc(allocator, file_size);
|
||||||
|
defer allocator.free(buffer);
|
||||||
|
|
||||||
|
var json_tree = try std.json.parseFromSlice(std.json.Value, allocator, buffer, .{});
|
||||||
|
defer json_tree.deinit();
|
||||||
|
|
||||||
|
const root = json_tree.value;
|
||||||
|
return try parseFn(root, allocator);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Specialized loaders
|
||||||
|
pub fn loadGameConfig(allocator: Allocator, filename: []const u8) ErrorSet!GameConfig {
|
||||||
|
return loadConfig(GameConfig, parseConfig, allocator, filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn loadChallengeConfig(allocator: Allocator, filename: []const u8) ErrorSet!ChallengeMazeConfig {
|
||||||
|
return loadConfig(ChallengeMazeConfig, parseChallengeConfig, allocator, filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn loadMapEntranceConfig(allocator: Allocator, filename: []const u8) ErrorSet!MapEntranceConfig {
|
||||||
|
return loadConfig(MapEntranceConfig, parseMapEntranceConfig, allocator, filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn loadMazePlaneConfig(allocator: Allocator, filename: []const u8) ErrorSet!MazePlaneConfig {
|
||||||
|
return loadConfig(MazePlaneConfig, parseMazePlaneConfig, allocator, filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parseConfig(root: std.json.Value, allocator: Allocator) ErrorSet!GameConfig {
|
||||||
|
const battle_config_json = root.object.get("battle_config").?;
|
||||||
|
var battle_config = BattleConfig{
|
||||||
|
.battle_id = @intCast(battle_config_json.object.get("battle_id").?.integer),
|
||||||
|
.stage_id = @intCast(battle_config_json.object.get("stage_id").?.integer),
|
||||||
|
.cycle_count = @intCast(battle_config_json.object.get("cycle_count").?.integer),
|
||||||
|
.monster_wave = ArrayList(ArrayList(u32)).init(allocator),
|
||||||
|
.monster_level = @intCast(battle_config_json.object.get("monster_level").?.integer),
|
||||||
|
.blessings = ArrayList(u32).init(allocator),
|
||||||
|
};
|
||||||
|
|
||||||
|
std.debug.print("loading config stageID = {}\n", .{battle_config.stage_id});
|
||||||
|
|
||||||
|
for (battle_config_json.object.get("monster_wave").?.array.items) |wave| {
|
||||||
|
var wave_list = ArrayList(u32).init(allocator);
|
||||||
|
for (wave.array.items) |monster| {
|
||||||
|
try wave_list.append(@intCast(monster.integer));
|
||||||
|
}
|
||||||
|
try battle_config.monster_wave.append(wave_list);
|
||||||
|
}
|
||||||
|
for (battle_config_json.object.get("blessings").?.array.items) |blessing| {
|
||||||
|
try battle_config.blessings.append(@intCast(blessing.integer));
|
||||||
|
}
|
||||||
|
|
||||||
|
var avatar_config = ArrayList(Avatar).init(allocator);
|
||||||
|
for (root.object.get("avatar_config").?.array.items) |avatar_json| {
|
||||||
|
var avatar = Avatar{
|
||||||
|
.id = @intCast(avatar_json.object.get("id").?.integer),
|
||||||
|
.hp = @intCast(avatar_json.object.get("hp").?.integer),
|
||||||
|
.sp = @intCast(avatar_json.object.get("sp").?.integer),
|
||||||
|
.level = @intCast(avatar_json.object.get("level").?.integer),
|
||||||
|
.promotion = @intCast(avatar_json.object.get("promotion").?.integer),
|
||||||
|
.rank = @intCast(avatar_json.object.get("rank").?.integer),
|
||||||
|
.lightcone = undefined,
|
||||||
|
.relics = ArrayList(Relic).init(allocator),
|
||||||
|
.use_technique = avatar_json.object.get("use_technique").?.bool,
|
||||||
|
};
|
||||||
|
|
||||||
|
const lightcone_json = avatar_json.object.get("lightcone").?;
|
||||||
|
avatar.lightcone = Lightcone{
|
||||||
|
.id = @intCast(lightcone_json.object.get("id").?.integer),
|
||||||
|
.rank = @intCast(lightcone_json.object.get("rank").?.integer),
|
||||||
|
.level = @intCast(lightcone_json.object.get("level").?.integer),
|
||||||
|
.promotion = @intCast(lightcone_json.object.get("promotion").?.integer),
|
||||||
|
};
|
||||||
|
|
||||||
|
for (avatar_json.object.get("relics").?.array.items) |relic_str| {
|
||||||
|
const relic = try parseRelic(relic_str.string, allocator);
|
||||||
|
try avatar.relics.append(relic);
|
||||||
|
}
|
||||||
|
|
||||||
|
try avatar_config.append(avatar);
|
||||||
|
}
|
||||||
|
|
||||||
|
return GameConfig{
|
||||||
|
.battle_config = battle_config,
|
||||||
|
.avatar_config = avatar_config,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
fn parseChallengeConfig(root: std.json.Value, allocator: Allocator) ErrorSet!ChallengeMazeConfig {
|
||||||
|
var challenge_config = ArrayList(ChallengeConfig).init(allocator);
|
||||||
|
for (root.object.get("challenge_config").?.array.items) |challenge_json| {
|
||||||
|
var challenge = ChallengeConfig{
|
||||||
|
.id = @intCast(challenge_json.object.get("ID").?.integer),
|
||||||
|
.npc_monster_id_list1 = ArrayList(u32).init(allocator),
|
||||||
|
.npc_monster_id_list2 = ArrayList(u32).init(allocator),
|
||||||
|
.event_id_list1 = ArrayList(u32).init(allocator),
|
||||||
|
.event_id_list2 = ArrayList(u32).init(allocator),
|
||||||
|
.map_entrance_id = @intCast(challenge_json.object.get("MapEntranceID").?.integer),
|
||||||
|
.map_entrance_id2 = @intCast(challenge_json.object.get("MapEntranceID2").?.integer),
|
||||||
|
.maze_group_id1 = @intCast(challenge_json.object.get("MazeGroupID1").?.integer),
|
||||||
|
.maze_group_id2 = @intCast(challenge_json.object.get("MazeGroupID2").?.integer),
|
||||||
|
};
|
||||||
|
for (challenge_json.object.get("NpcMonsterIDList1").?.array.items) |npc1| {
|
||||||
|
try challenge.npc_monster_id_list1.append(@intCast(npc1.integer));
|
||||||
|
}
|
||||||
|
for (challenge_json.object.get("NpcMonsterIDList2").?.array.items) |npc2| {
|
||||||
|
try challenge.npc_monster_id_list2.append(@intCast(npc2.integer));
|
||||||
|
}
|
||||||
|
for (challenge_json.object.get("EventIDList1").?.array.items) |event1| {
|
||||||
|
try challenge.event_id_list1.append(@intCast(event1.integer));
|
||||||
|
}
|
||||||
|
for (challenge_json.object.get("EventIDList2").?.array.items) |event2| {
|
||||||
|
try challenge.event_id_list2.append(@intCast(event2.integer));
|
||||||
|
}
|
||||||
|
try challenge_config.append(challenge);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ChallengeMazeConfig{
|
||||||
|
.challenge_config = challenge_config,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
fn parseMapEntranceConfig(root: std.json.Value, allocator: Allocator) ErrorSet!MapEntranceConfig {
|
||||||
|
var map_entrance_config = ArrayList(MapEntrance).init(allocator);
|
||||||
|
for (root.object.get("map_entrance_config").?.array.items) |mapEntrance| {
|
||||||
|
var entrance = MapEntrance{
|
||||||
|
.id = @intCast(mapEntrance.object.get("ID").?.integer),
|
||||||
|
.floor_id = @intCast(mapEntrance.object.get("FloorID").?.integer),
|
||||||
|
.plane_id = @intCast(mapEntrance.object.get("PlaneID").?.integer),
|
||||||
|
.begin_main_mission_idlist = ArrayList(u32).init(allocator),
|
||||||
|
.finish_main_mission_idlist = ArrayList(u32).init(allocator),
|
||||||
|
.finish_sub_mission_idlist = ArrayList(u32).init(allocator),
|
||||||
|
};
|
||||||
|
for (mapEntrance.object.get("BeginMainMissionList").?.array.items) |id| {
|
||||||
|
try entrance.begin_main_mission_idlist.append(@intCast(id.integer));
|
||||||
|
}
|
||||||
|
for (mapEntrance.object.get("FinishMainMissionList").?.array.items) |id| {
|
||||||
|
try entrance.finish_main_mission_idlist.append(@intCast(id.integer));
|
||||||
|
}
|
||||||
|
for (mapEntrance.object.get("FinishSubMissionList").?.array.items) |id| {
|
||||||
|
try entrance.finish_sub_mission_idlist.append(@intCast(id.integer));
|
||||||
|
}
|
||||||
|
try map_entrance_config.append(entrance);
|
||||||
|
}
|
||||||
|
|
||||||
|
return MapEntranceConfig{
|
||||||
|
.map_entrance_config = map_entrance_config,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
fn parseMazePlaneConfig(root: std.json.Value, allocator: Allocator) ErrorSet!MazePlaneConfig {
|
||||||
|
var maze_plane_config = ArrayList(MazePlane).init(allocator);
|
||||||
|
for (root.object.get("maze_plane_config").?.array.items) |id| {
|
||||||
|
var maze = MazePlane{
|
||||||
|
.start_floor_id = @intCast(id.object.get("StartFloorID").?.integer),
|
||||||
|
.challenge_plane_id = @intCast(id.object.get("PlaneID").?.integer),
|
||||||
|
.world_id = @intCast(id.object.get("WorldID").?.integer),
|
||||||
|
.floor_id_list = ArrayList(u32).init(allocator),
|
||||||
|
};
|
||||||
|
for (id.object.get("FloorIDList").?.array.items) |list| {
|
||||||
|
try maze.floor_id_list.append(@intCast(list.integer));
|
||||||
|
}
|
||||||
|
try maze_plane_config.append(maze);
|
||||||
|
}
|
||||||
|
|
||||||
|
return MazePlaneConfig{
|
||||||
|
.maze_plane_config = maze_plane_config,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
fn parseRelic(relic_str: []const u8, allocator: Allocator) !Relic {
|
||||||
|
var tokens = ArrayList([]const u8).init(allocator);
|
||||||
|
defer tokens.deinit();
|
||||||
|
|
||||||
|
var iterator = std.mem.tokenize(u8, relic_str, ",");
|
||||||
|
|
||||||
|
while (iterator.next()) |token| {
|
||||||
|
try tokens.append(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
const tokens_slice = tokens.items;
|
||||||
|
|
||||||
|
if (tokens_slice.len < 8) {
|
||||||
|
std.debug.print("relic parsing error: {s}\n", .{relic_str});
|
||||||
|
return error.InsufficientTokens;
|
||||||
|
}
|
||||||
|
|
||||||
|
const stat1 = try parseStatCount(tokens_slice[4]);
|
||||||
|
const stat2 = try parseStatCount(tokens_slice[5]);
|
||||||
|
const stat3 = try parseStatCount(tokens_slice[6]);
|
||||||
|
const stat4 = try parseStatCount(tokens_slice[7]);
|
||||||
|
|
||||||
|
const relic = Relic{
|
||||||
|
.id = try std.fmt.parseInt(u32, tokens_slice[0], 10),
|
||||||
|
.level = try std.fmt.parseInt(u32, tokens_slice[1], 10),
|
||||||
|
.main_affix_id = try std.fmt.parseInt(u32, tokens_slice[2], 10),
|
||||||
|
.sub_count = try std.fmt.parseInt(u32, tokens_slice[3], 10),
|
||||||
|
.stat1 = stat1.stat,
|
||||||
|
.cnt1 = stat1.count,
|
||||||
|
.step1 = stat1.step,
|
||||||
|
.stat2 = stat2.stat,
|
||||||
|
.cnt2 = stat2.count,
|
||||||
|
.step2 = stat2.step,
|
||||||
|
.stat3 = stat3.stat,
|
||||||
|
.cnt3 = stat3.count,
|
||||||
|
.step3 = stat3.step,
|
||||||
|
.stat4 = stat4.stat,
|
||||||
|
.cnt4 = stat4.count,
|
||||||
|
.step4 = stat4.step,
|
||||||
|
};
|
||||||
|
|
||||||
|
return relic;
|
||||||
|
}
|
||||||
|
|
||||||
|
const StatCount = struct {
|
||||||
|
stat: u32,
|
||||||
|
count: u32,
|
||||||
|
step: u32,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn parseStatCount(token: []const u8) !StatCount {
|
||||||
|
if (std.mem.indexOfScalar(u8, token, ':')) |first_colon| {
|
||||||
|
if (std.mem.indexOfScalar(u8, token[first_colon + 1 ..], ':')) |second_colon_offset| {
|
||||||
|
const second_colon = first_colon + 1 + second_colon_offset;
|
||||||
|
const stat = try std.fmt.parseInt(u32, token[0..first_colon], 10);
|
||||||
|
const count = try std.fmt.parseInt(u32, token[first_colon + 1 .. second_colon], 10);
|
||||||
|
const step = try std.fmt.parseInt(u32, token[second_colon + 1 ..], 10);
|
||||||
|
return StatCount{ .stat = stat, .count = count, .step = step };
|
||||||
|
} else {
|
||||||
|
return error.InvalidFormat;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return error.InvalidFormat;
|
||||||
|
}
|
||||||
|
}
|
29
gameserver/src/services/events.zig
Normal file
29
gameserver/src/services/events.zig
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const protocol = @import("protocol");
|
||||||
|
const Session = @import("../Session.zig");
|
||||||
|
const Packet = @import("../Packet.zig");
|
||||||
|
const Data = @import("../data.zig");
|
||||||
|
|
||||||
|
const ArrayList = std.ArrayList;
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
const CmdID = protocol.CmdID;
|
||||||
|
|
||||||
|
pub fn onGetActivity(session: *Session, _: *const Packet, allocator: Allocator) !void {
|
||||||
|
var rsp = protocol.GetActivityScheduleConfigScRsp.init(allocator);
|
||||||
|
|
||||||
|
for (Data.EventList) |id| {
|
||||||
|
var activ_list = protocol.ActivityScheduleData.init(allocator);
|
||||||
|
activ_list.begin_time = 0;
|
||||||
|
activ_list.end_time = 1924992000;
|
||||||
|
if (id >= 100000) {
|
||||||
|
activ_list.activity_id = ((id % 100000) * 100) + 1;
|
||||||
|
} else {
|
||||||
|
activ_list.activity_id = id * 100 + 1;
|
||||||
|
}
|
||||||
|
activ_list.panel_id = id;
|
||||||
|
try rsp.schedule_data.append(activ_list);
|
||||||
|
}
|
||||||
|
rsp.retcode = 0;
|
||||||
|
|
||||||
|
try session.send(CmdID.CmdGetActivityScheduleConfigScRsp, rsp);
|
||||||
|
}
|
222
gameserver/src/services/gacha.zig
Normal file
222
gameserver/src/services/gacha.zig
Normal file
|
@ -0,0 +1,222 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const protocol = @import("protocol");
|
||||||
|
const Session = @import("../Session.zig");
|
||||||
|
const Packet = @import("../Packet.zig");
|
||||||
|
const Data = @import("../data.zig");
|
||||||
|
const GachaData = @import("../commands/value.zig");
|
||||||
|
|
||||||
|
const ArrayList = std.ArrayList;
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
const CmdID = protocol.CmdID;
|
||||||
|
|
||||||
|
// function to check the list if true
|
||||||
|
fn isInList(id: u32, list: []const u32) bool {
|
||||||
|
for (list) |item| {
|
||||||
|
if (item == id) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn onGetGachaInfo(session: *Session, _: *const Packet, allocator: Allocator) !void {
|
||||||
|
var info = ArrayList(protocol.GachaCeilingAvatar).init(allocator);
|
||||||
|
for (GachaData.StandardBanner) |id| {
|
||||||
|
try info.appendSlice(&[_]protocol.GachaCeilingAvatar{
|
||||||
|
.{ .RepeatedCnt = 300, .AvatarId = id },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
var gacha_info = protocol.GachaInfo.init(allocator);
|
||||||
|
gacha_info.begin_time = 0;
|
||||||
|
gacha_info.end_time = 2524608000;
|
||||||
|
gacha_info.gacha_ceiling = .{
|
||||||
|
.avatar_list = info,
|
||||||
|
.is_claimed = false,
|
||||||
|
.ceiling_num = 200,
|
||||||
|
};
|
||||||
|
gacha_info.KMNJNMJFGBG = 1;
|
||||||
|
gacha_info.GDIFAAHIFBH = 3;
|
||||||
|
gacha_info.gacha_id = 1001; // standard banner
|
||||||
|
|
||||||
|
var rsp = protocol.GetGachaInfoScRsp.init(allocator);
|
||||||
|
|
||||||
|
rsp.retcode = 0;
|
||||||
|
rsp.DJNDMNPEBKA = 20;
|
||||||
|
rsp.NOPBEBKHIKA = 20;
|
||||||
|
rsp.NBELNOIPOEK = 900;
|
||||||
|
rsp.gacha_random = 0;
|
||||||
|
try rsp.gacha_info_list.append(gacha_info);
|
||||||
|
|
||||||
|
try session.send(CmdID.CmdGetGachaInfoScRsp, rsp);
|
||||||
|
}
|
||||||
|
pub fn onBuyGoods(session: *Session, packet: *const Packet, allocator: Allocator) !void {
|
||||||
|
const req = try packet.getProto(protocol.BuyGoodsCsReq, allocator);
|
||||||
|
|
||||||
|
var rsp = protocol.BuyGoodsScRsp.init(allocator);
|
||||||
|
var item = ArrayList(protocol.Item).init(allocator);
|
||||||
|
|
||||||
|
try item.appendSlice(&[_]protocol.Item{.{
|
||||||
|
.ItemId = 101,
|
||||||
|
.Num = 100,
|
||||||
|
}});
|
||||||
|
|
||||||
|
rsp.retcode = 0;
|
||||||
|
rsp.GoodsId = req.goods_id;
|
||||||
|
rsp.GoodsBuyTimes = req.goods_num;
|
||||||
|
rsp.ShopId = 0;
|
||||||
|
rsp.ReturnItemList = .{ .ItemList_ = item };
|
||||||
|
|
||||||
|
try session.send(CmdID.CmdBuyGoodsScRsp, rsp);
|
||||||
|
}
|
||||||
|
pub fn onGetShopList(session: *Session, _: *const Packet, allocator: Allocator) !void {
|
||||||
|
var rsp = protocol.GetShopListScRsp.init(allocator);
|
||||||
|
var shop = ArrayList(protocol.Shop).init(allocator);
|
||||||
|
var goods = ArrayList(protocol.Goods).init(allocator);
|
||||||
|
|
||||||
|
try shop.appendSlice(&[_]protocol.Shop{.{
|
||||||
|
.ShopId = 1000,
|
||||||
|
.GoodsList = goods,
|
||||||
|
}});
|
||||||
|
try goods.appendSlice(&[_]protocol.Goods{.{
|
||||||
|
.GoodsId = 101001,
|
||||||
|
.ItemId = 101,
|
||||||
|
.BuyTimes = 0,
|
||||||
|
}});
|
||||||
|
|
||||||
|
rsp.retcode = 0;
|
||||||
|
rsp.ShopType = 101;
|
||||||
|
rsp.ShopList = shop;
|
||||||
|
|
||||||
|
try session.send(CmdID.CmdGetShopListScRsp, rsp);
|
||||||
|
}
|
||||||
|
pub fn onExchangeHcoin(session: *Session, packet: *const Packet, allocator: Allocator) !void {
|
||||||
|
const req = try packet.getProto(protocol.ExchangeHcoinCsReq, allocator);
|
||||||
|
|
||||||
|
var rsp = protocol.ExchangeHcoinScRsp.init(allocator);
|
||||||
|
rsp.Num = req.Num;
|
||||||
|
rsp.retcode = 0;
|
||||||
|
|
||||||
|
try session.send(CmdID.CmdExchangeHcoinScRsp, rsp);
|
||||||
|
}
|
||||||
|
|
||||||
|
var five_star_pity: u32 = 0;
|
||||||
|
var four_star_pity: u32 = 0;
|
||||||
|
var guaranteed_five_star_rate_up: bool = false;
|
||||||
|
var guaranteed_four_star_rate_up: bool = false;
|
||||||
|
var avatar_list_cached: ?std.ArrayList(u32) = null;
|
||||||
|
var lightcone_list_3_cached: ?std.ArrayList(u32) = null;
|
||||||
|
var lightcone_list_4_cached: ?std.ArrayList(u32) = null;
|
||||||
|
fn pow(base: f64, exp: f64) f64 {
|
||||||
|
return @exp(exp * @log(base));
|
||||||
|
}
|
||||||
|
fn getFiveStarRate(gacha_count: u32) f64 {
|
||||||
|
if (gacha_count < 21) {
|
||||||
|
return 0.02;
|
||||||
|
}
|
||||||
|
if (gacha_count < 72) {
|
||||||
|
return 0.008;
|
||||||
|
}
|
||||||
|
const excess_pulls = @as(f64, @floatFromInt(gacha_count - 71));
|
||||||
|
return 0.008 + (1.0 - 0.008) * pow(excess_pulls / 18.0, 2.8);
|
||||||
|
}
|
||||||
|
fn getFourStarRate(gacha_count: u32) f64 {
|
||||||
|
if (gacha_count < 6) {
|
||||||
|
return 0.055;
|
||||||
|
}
|
||||||
|
const excess_pulls = @as(f64, @floatFromInt(gacha_count - 5));
|
||||||
|
return 0.055 + (1.0 - 0.055) * pow(excess_pulls / 3.5, 2.2);
|
||||||
|
}
|
||||||
|
fn pickRandomId(rnd: *std.rand.Random, banner: []const u32) u32 {
|
||||||
|
return banner[rnd.int(usize) % banner.len];
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn onDoGacha(session: *Session, packet: *const Packet, allocator: std.mem.Allocator) !void {
|
||||||
|
const req = try packet.getProto(protocol.DoGachaCsReq, allocator);
|
||||||
|
var rsp = protocol.DoGachaScRsp.init(allocator);
|
||||||
|
var seed: u64 = undefined;
|
||||||
|
try std.posix.getrandom(std.mem.asBytes(&seed));
|
||||||
|
var prng = std.rand.DefaultPrng.init(seed);
|
||||||
|
var rnd = prng.random();
|
||||||
|
var selected_ids = std.ArrayList(u32).init(allocator);
|
||||||
|
defer selected_ids.deinit();
|
||||||
|
var got_four_star = false;
|
||||||
|
for (0..req.gacha_num) |_| {
|
||||||
|
const five_star_rate = getFiveStarRate(five_star_pity);
|
||||||
|
const four_star_rate = getFourStarRate(four_star_pity);
|
||||||
|
const random_value = rnd.float(f64);
|
||||||
|
var selected_banner: []const u32 = &Data.LightconeList_3;
|
||||||
|
var is_five_star = false;
|
||||||
|
var is_four_star = false;
|
||||||
|
if (random_value < five_star_rate or five_star_pity == 89) {
|
||||||
|
is_five_star = true;
|
||||||
|
five_star_pity = 0;
|
||||||
|
if (guaranteed_five_star_rate_up) {
|
||||||
|
selected_banner = &GachaData.RateUp;
|
||||||
|
guaranteed_five_star_rate_up = false;
|
||||||
|
} else {
|
||||||
|
if (rnd.boolean()) {
|
||||||
|
selected_banner = &GachaData.RateUp;
|
||||||
|
} else {
|
||||||
|
selected_banner = &GachaData.StandardBanner;
|
||||||
|
guaranteed_five_star_rate_up = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (four_star_pity == 9 or random_value < (five_star_rate + four_star_rate)) {
|
||||||
|
is_four_star = true;
|
||||||
|
four_star_pity = 0;
|
||||||
|
got_four_star = true;
|
||||||
|
|
||||||
|
if (guaranteed_four_star_rate_up or rnd.float(f64) < 0.70) {
|
||||||
|
selected_banner = &GachaData.RateUpFourStars;
|
||||||
|
guaranteed_four_star_rate_up = false;
|
||||||
|
} else {
|
||||||
|
if (rnd.boolean()) {
|
||||||
|
selected_banner = &Data.AvatarList;
|
||||||
|
} else {
|
||||||
|
selected_banner = &Data.LightconeList_4;
|
||||||
|
}
|
||||||
|
guaranteed_four_star_rate_up = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
four_star_pity += 1;
|
||||||
|
}
|
||||||
|
five_star_pity += 1;
|
||||||
|
try selected_ids.append(pickRandomId(&rnd, selected_banner));
|
||||||
|
}
|
||||||
|
if (req.gacha_num > 1 and !got_four_star) {
|
||||||
|
selected_ids.items[rnd.int(usize) % selected_ids.items.len] = pickRandomId(&rnd, &GachaData.RateUpFourStars);
|
||||||
|
}
|
||||||
|
for (selected_ids.items) |id| {
|
||||||
|
var gacha_item = protocol.GachaItem.init(allocator);
|
||||||
|
gacha_item.gacha_item = .{ .ItemId = id };
|
||||||
|
gacha_item.is_new = false;
|
||||||
|
var back_item = std.ArrayList(protocol.Item).init(allocator);
|
||||||
|
var transfer_item = std.ArrayList(protocol.Item).init(allocator);
|
||||||
|
if (id < 10000) {
|
||||||
|
if (isInList(id, &GachaData.RateUp) or isInList(id, &GachaData.StandardBanner)) {
|
||||||
|
try transfer_item.appendSlice(&[_]protocol.Item{
|
||||||
|
.{ .ItemId = id + 10000, .Num = 1 },
|
||||||
|
.{ .ItemId = 252, .Num = 20 },
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
try transfer_item.append(.{ .ItemId = 252, .Num = 20 });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try back_item.append(.{ .ItemId = 252, .Num = 20 });
|
||||||
|
gacha_item.transfer_item_list = .{ .ItemList_ = transfer_item };
|
||||||
|
gacha_item.token_item = .{ .ItemList_ = back_item };
|
||||||
|
try rsp.gacha_item_list.append(gacha_item);
|
||||||
|
}
|
||||||
|
rsp.gacha_num = req.gacha_num;
|
||||||
|
rsp.gacha_id = req.gacha_id;
|
||||||
|
rsp.ceiling_num = 200;
|
||||||
|
rsp.KMNJNMJFGBG = 1;
|
||||||
|
rsp.NOPBEBKHIKA = 20;
|
||||||
|
rsp.GDIFAAHIFBH = 3;
|
||||||
|
rsp.retcode = 0;
|
||||||
|
|
||||||
|
std.debug.print("FIVE STAR PITY: {}, (RATE: {d:.4}%)\n", .{ five_star_pity, getFiveStarRate(five_star_pity) * 100.0 });
|
||||||
|
std.debug.print("FOUR STAR PITY: {}, (RATE: {d:.4}%)\n", .{ four_star_pity, getFourStarRate(four_star_pity) * 100.0 });
|
||||||
|
|
||||||
|
try session.send(protocol.CmdID.CmdDoGachaScRsp, rsp);
|
||||||
|
}
|
90
gameserver/src/services/item.zig
Normal file
90
gameserver/src/services/item.zig
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const protocol = @import("protocol");
|
||||||
|
const Session = @import("../Session.zig");
|
||||||
|
const Packet = @import("../Packet.zig");
|
||||||
|
const Config = @import("config.zig");
|
||||||
|
const Data = @import("../data.zig");
|
||||||
|
const LineupManager = @import("../manager/lineup_mgr.zig").LineupManager;
|
||||||
|
const ArrayList = std.ArrayList;
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
const CmdID = protocol.CmdID;
|
||||||
|
|
||||||
|
pub fn onGetBag(session: *Session, _: *const Packet, allocator: Allocator) !void {
|
||||||
|
const config = try Config.loadGameConfig(allocator, "config.json");
|
||||||
|
var generator = UidGenerator().init();
|
||||||
|
|
||||||
|
// fake item inventory
|
||||||
|
// TODO: make real one
|
||||||
|
var rsp = protocol.GetBagScRsp.init(allocator);
|
||||||
|
rsp.equipment_list = ArrayList(protocol.Equipment).init(allocator);
|
||||||
|
rsp.relic_list = ArrayList(protocol.Relic).init(allocator);
|
||||||
|
|
||||||
|
for (Data.ItemList) |tid| {
|
||||||
|
try rsp.material_list.append(.{ .tid = tid, .Num = 100 });
|
||||||
|
}
|
||||||
|
|
||||||
|
for (config.avatar_config.items) |avatarConf| {
|
||||||
|
// lc
|
||||||
|
const lc = protocol.Equipment{
|
||||||
|
.unique_id = generator.nextId(),
|
||||||
|
.tid = avatarConf.lightcone.id, // id
|
||||||
|
.is_protected = true, // lock
|
||||||
|
.level = avatarConf.lightcone.level,
|
||||||
|
.rank = avatarConf.lightcone.rank,
|
||||||
|
.promotion = avatarConf.lightcone.promotion,
|
||||||
|
.equip_avatar_id = avatarConf.id, // base avatar id
|
||||||
|
};
|
||||||
|
try rsp.equipment_list.append(lc);
|
||||||
|
|
||||||
|
// relics
|
||||||
|
for (avatarConf.relics.items) |input| {
|
||||||
|
var r = protocol.Relic{
|
||||||
|
.tid = input.id, // id
|
||||||
|
.main_affix_id = input.main_affix_id,
|
||||||
|
.unique_id = generator.nextId(),
|
||||||
|
.exp = 0,
|
||||||
|
.equip_avatar_id = avatarConf.id, // base avatar id
|
||||||
|
.is_protected = true, // lock
|
||||||
|
.level = input.level,
|
||||||
|
.sub_affix_list = ArrayList(protocol.RelicAffix).init(allocator),
|
||||||
|
.reforge_sub_affix_list = ArrayList(protocol.RelicAffix).init(allocator),
|
||||||
|
};
|
||||||
|
try r.sub_affix_list.append(protocol.RelicAffix{ .affix_id = input.stat1, .cnt = input.cnt1, .step = input.step1 });
|
||||||
|
try r.sub_affix_list.append(protocol.RelicAffix{ .affix_id = input.stat2, .cnt = input.cnt2, .step = input.step2 });
|
||||||
|
try r.sub_affix_list.append(protocol.RelicAffix{ .affix_id = input.stat3, .cnt = input.cnt3, .step = input.step3 });
|
||||||
|
try r.sub_affix_list.append(protocol.RelicAffix{ .affix_id = input.stat4, .cnt = input.cnt4, .step = input.step4 });
|
||||||
|
try rsp.relic_list.append(r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try session.send(CmdID.CmdGetBagScRsp, rsp);
|
||||||
|
}
|
||||||
|
pub fn onUseItem(session: *Session, packet: *const Packet, allocator: Allocator) !void {
|
||||||
|
const req = try packet.getProto(protocol.UseItemCsReq, allocator);
|
||||||
|
var rsp = protocol.UseItemScRsp.init(allocator);
|
||||||
|
rsp.use_item_id = req.use_item_id;
|
||||||
|
rsp.use_item_count = req.use_item_count;
|
||||||
|
rsp.retcode = 0;
|
||||||
|
var sync = protocol.SyncLineupNotify.init(allocator);
|
||||||
|
var lineup_mgr = LineupManager.init(allocator);
|
||||||
|
const lineup = try lineup_mgr.createLineup();
|
||||||
|
sync.Lineup = lineup;
|
||||||
|
try session.send(CmdID.CmdSyncLineupNotify, sync);
|
||||||
|
try session.send(CmdID.CmdUseItemScRsp, rsp);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn UidGenerator() type {
|
||||||
|
return struct {
|
||||||
|
current_id: u32,
|
||||||
|
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
pub fn init() Self {
|
||||||
|
return Self{ .current_id = 0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn nextId(self: *Self) u32 {
|
||||||
|
self.current_id +%= 1; // Using wrapping addition
|
||||||
|
return self.current_id;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
59
gameserver/src/services/lineup.zig
Normal file
59
gameserver/src/services/lineup.zig
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const protocol = @import("protocol");
|
||||||
|
const Session = @import("../Session.zig");
|
||||||
|
const Packet = @import("../Packet.zig");
|
||||||
|
const Config = @import("config.zig");
|
||||||
|
const LineupManager = @import("../manager/lineup_mgr.zig").LineupManager;
|
||||||
|
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
const CmdID = protocol.CmdID;
|
||||||
|
|
||||||
|
pub fn onGetCurLineupData(session: *Session, _: *const Packet, allocator: Allocator) !void {
|
||||||
|
var lineup_mgr = LineupManager.init(allocator);
|
||||||
|
const lineup = try lineup_mgr.createLineup();
|
||||||
|
try session.send(CmdID.CmdGetCurLineupDataScRsp, protocol.GetCurLineupDataScRsp{
|
||||||
|
.retcode = 0,
|
||||||
|
.lineup = lineup,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn onChangeLineupLeader(session: *Session, packet: *const Packet, allocator: Allocator) !void {
|
||||||
|
const req = try packet.getProto(protocol.ChangeLineupLeaderCsReq, allocator);
|
||||||
|
try session.send(CmdID.CmdChangeLineupLeaderScRsp, protocol.ChangeLineupLeaderScRsp{
|
||||||
|
.slot = req.slot,
|
||||||
|
.retcode = 0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn onReplaceLineup(session: *Session, packet: *const Packet, allocator: Allocator) !void {
|
||||||
|
const req = try packet.getProto(protocol.ReplaceLineupCsReq, allocator);
|
||||||
|
var lineup = protocol.LineupInfo.init(allocator);
|
||||||
|
lineup.mp = 5;
|
||||||
|
lineup.max_mp = 5;
|
||||||
|
lineup.name = .{ .Const = "CastoriceSR" };
|
||||||
|
for (req.lineup_slot_list.items) |ok| {
|
||||||
|
const avatar = protocol.LineupAvatar{
|
||||||
|
.id = ok.id,
|
||||||
|
.slot = ok.slot,
|
||||||
|
.satiety = 0,
|
||||||
|
.hp = 10000,
|
||||||
|
.avatar_type = protocol.AvatarType.AVATAR_FORMAL_TYPE,
|
||||||
|
.sp_bar = .{ .sp_cur = 10000, .sp_max = 10000 },
|
||||||
|
};
|
||||||
|
try lineup.avatar_list.append(avatar);
|
||||||
|
}
|
||||||
|
var rsp = protocol.SyncLineupNotify.init(allocator);
|
||||||
|
rsp.Lineup = lineup;
|
||||||
|
try session.send(CmdID.CmdSyncLineupNotify, rsp);
|
||||||
|
try session.send(CmdID.CmdReplaceLineupScRsp, protocol.ReplaceLineupScRsp{
|
||||||
|
.retcode = 0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
pub fn onSetLineupName(session: *Session, packet: *const Packet, allocator: Allocator) !void {
|
||||||
|
const req = try packet.getProto(protocol.SetLineupNameCsReq, allocator);
|
||||||
|
try session.send(CmdID.CmdSetLineupNameScRsp, protocol.SetLineupNameScRsp{
|
||||||
|
.index = req.index,
|
||||||
|
.name = req.name,
|
||||||
|
.retcode = 0,
|
||||||
|
});
|
||||||
|
}
|
81
gameserver/src/services/login.zig
Normal file
81
gameserver/src/services/login.zig
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const protocol = @import("protocol");
|
||||||
|
const Session = @import("../Session.zig");
|
||||||
|
const Packet = @import("../Packet.zig");
|
||||||
|
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
const ArrayList = std.ArrayList;
|
||||||
|
const CmdID = protocol.CmdID;
|
||||||
|
|
||||||
|
pub fn onPlayerGetToken(session: *Session, _: *const Packet, allocator: Allocator) !void {
|
||||||
|
var rsp = protocol.PlayerGetTokenScRsp.init(allocator);
|
||||||
|
|
||||||
|
rsp.retcode = 0;
|
||||||
|
rsp.uid = 1;
|
||||||
|
try session.send(CmdID.CmdPlayerGetTokenScRsp, rsp);
|
||||||
|
}
|
||||||
|
pub fn onPlayerLogin(session: *Session, packet: *const Packet, allocator: Allocator) !void {
|
||||||
|
const req = try packet.getProto(protocol.PlayerLoginCsReq, allocator);
|
||||||
|
|
||||||
|
var basic_info = protocol.PlayerBasicInfo.init(allocator);
|
||||||
|
basic_info.stamina = 300;
|
||||||
|
basic_info.level = 70;
|
||||||
|
basic_info.nickname = .{ .Const = "ReversedRooms" };
|
||||||
|
basic_info.world_level = 6;
|
||||||
|
basic_info.mcoin = 99999990;
|
||||||
|
basic_info.hcoin = 99999990; //Jade
|
||||||
|
basic_info.scoin = 99999990; //Money
|
||||||
|
|
||||||
|
var rsp = protocol.PlayerLoginScRsp.init(allocator);
|
||||||
|
rsp.retcode = 0;
|
||||||
|
rsp.login_random = req.login_random;
|
||||||
|
rsp.stamina = 300;
|
||||||
|
rsp.basic_info = basic_info;
|
||||||
|
|
||||||
|
try session.send(CmdID.CmdPlayerLoginScRsp, rsp);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn onPlayerLoginFinish(session: *Session, _: *const Packet, allocator: Allocator) !void {
|
||||||
|
const content = [_]u32{
|
||||||
|
200001, 200002, 200003, 200004, 200005, 200006,
|
||||||
|
150017, 150015, 150021, 150018, 130011, 130012,
|
||||||
|
130013, 150025, 140006, 150026, 130014, 150034,
|
||||||
|
150029, 150035, 150039,
|
||||||
|
};
|
||||||
|
|
||||||
|
var package_data = protocol.ContentPackageData.init(allocator);
|
||||||
|
package_data.cur_content_id = 0;
|
||||||
|
for (content) |id| {
|
||||||
|
try package_data.content_package_list.append(protocol.ContentPackageInfo{
|
||||||
|
.content_id = id,
|
||||||
|
.status = protocol.ContentPackageStatus.ContentPackageStatus_Finished,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var noti = protocol.UnlockAvatarSkinScNotify.init(allocator);
|
||||||
|
noti.skin_id = 1100101;
|
||||||
|
try session.send(CmdID.CmdUnlockAvatarSkinScNotify, noti);
|
||||||
|
var phone = protocol.UnlockPhoneCaseScNotify.init(allocator);
|
||||||
|
phone.phone_case_id = 254000;
|
||||||
|
try session.send(CmdID.CmdUnlockPhoneCaseScNotify, phone);
|
||||||
|
try session.send(CmdID.CmdContentPackageSyncDataScNotify, protocol.ContentPackageSyncDataScNotify{
|
||||||
|
.data = package_data,
|
||||||
|
});
|
||||||
|
try session.send(CmdID.CmdPlayerLoginFinishScRsp, protocol.PlayerLoginFinishScRsp{
|
||||||
|
.retcode = 0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn onContentPackageGetData(session: *Session, _: *const Packet, allocator: Allocator) !void {
|
||||||
|
var rsp = protocol.ContentPackageGetDataScRsp.init(allocator);
|
||||||
|
rsp.retcode = 0;
|
||||||
|
try session.send(CmdID.CmdContentPackageGetDataScRsp, rsp);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn onSetClientPaused(session: *Session, packet: *const Packet, allocator: Allocator) !void {
|
||||||
|
const req = try packet.getProto(protocol.SetClientPausedCsReq, allocator);
|
||||||
|
try session.send(CmdID.CmdSetClientPausedScRsp, protocol.SetClientPausedScRsp{
|
||||||
|
.retcode = 0,
|
||||||
|
.paused = req.paused,
|
||||||
|
});
|
||||||
|
}
|
39
gameserver/src/services/mail.zig
Normal file
39
gameserver/src/services/mail.zig
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const protocol = @import("protocol");
|
||||||
|
const Session = @import("../Session.zig");
|
||||||
|
const Packet = @import("../Packet.zig");
|
||||||
|
|
||||||
|
const ArrayList = std.ArrayList;
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
const CmdID = protocol.CmdID;
|
||||||
|
|
||||||
|
const B64Decoder = std.base64.standard.Decoder;
|
||||||
|
|
||||||
|
pub fn onGetMail(session: *Session, _: *const Packet, allocator: Allocator) !void {
|
||||||
|
var rsp = protocol.GetMailScRsp.init(allocator);
|
||||||
|
var item_attachment = ArrayList(protocol.Item).init(allocator);
|
||||||
|
try item_attachment.appendSlice(&[_]protocol.Item{
|
||||||
|
.{ .ItemId = 1407, .Num = 1 },
|
||||||
|
});
|
||||||
|
var mail = protocol.ClientMail.init(allocator);
|
||||||
|
mail.Sender = .{ .Const = "Castorice" };
|
||||||
|
mail.Title = .{ .Const = "Readme" };
|
||||||
|
mail.IsRead = false;
|
||||||
|
mail.id = 1;
|
||||||
|
mail.Content = .{ .Const = "CastoriceSR is a free and open-source sofware\nJoin our discord: https://discord.gg/reversedrooms\nUse https://yunlisr-relic-builder.vercel.app to setup relic :Đ\n" };
|
||||||
|
mail.Time = 1723334400;
|
||||||
|
mail.ExpireTime = 17186330890;
|
||||||
|
mail.MailType = protocol.MailType.MAIL_TYPE_STAR;
|
||||||
|
mail.Attachment = .{ .ItemList_ = item_attachment };
|
||||||
|
|
||||||
|
var mail_list = ArrayList(protocol.ClientMail).init(allocator);
|
||||||
|
try mail_list.append(mail);
|
||||||
|
|
||||||
|
rsp.TotalNum = 1;
|
||||||
|
rsp.IsEnd = true;
|
||||||
|
rsp.Start = 0;
|
||||||
|
rsp.retcode = 0;
|
||||||
|
rsp.MailList = mail_list;
|
||||||
|
|
||||||
|
try session.send(CmdID.CmdGetMailScRsp, rsp);
|
||||||
|
}
|
42
gameserver/src/services/misc copy.zig
Normal file
42
gameserver/src/services/misc copy.zig
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const protocol = @import("protocol");
|
||||||
|
const Session = @import("../Session.zig");
|
||||||
|
const Packet = @import("../Packet.zig");
|
||||||
|
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
const CmdID = protocol.CmdID;
|
||||||
|
|
||||||
|
const B64Decoder = std.base64.standard.Decoder;
|
||||||
|
|
||||||
|
pub fn onPlayerHeartBeat(session: *Session, packet: *const Packet, allocator: Allocator) !void {
|
||||||
|
const req = try packet.getProto(protocol.PlayerHeartBeatCsReq, allocator);
|
||||||
|
const lua_code =
|
||||||
|
\\local function setTextComponent(path, newText)
|
||||||
|
\\ local obj = CS.UnityEngine.GameObject.Find(path)
|
||||||
|
\\ if obj then
|
||||||
|
\\ local textComponent = obj:GetComponentInChildren(typeof(CS.RPG.Client.LocalizedText))
|
||||||
|
\\ if textComponent then
|
||||||
|
\\ textComponent.text = newText
|
||||||
|
\\ end
|
||||||
|
\\ end
|
||||||
|
\\end
|
||||||
|
\\
|
||||||
|
\\setTextComponent("UIRoot/AboveDialog/BetaHintDialog(Clone)", "<color=#ffc800>CipherSR is a free and open source software.</color>")
|
||||||
|
\\setTextComponent("VersionText", "<color=#ffc800>Visit discord.gg/reversedrooms for more info!</color>")
|
||||||
|
;
|
||||||
|
const data = protocol.ClientDownloadData{
|
||||||
|
.version = 51,
|
||||||
|
.time = @intCast(std.time.milliTimestamp()),
|
||||||
|
.data = .{ .Owned = .{
|
||||||
|
.allocator = allocator,
|
||||||
|
.str = try allocator.dupe(u8, lua_code),
|
||||||
|
} },
|
||||||
|
};
|
||||||
|
const rsp = protocol.PlayerHeartBeatScRsp{
|
||||||
|
.retcode = 0,
|
||||||
|
.client_time_ms = req.client_time_ms,
|
||||||
|
.server_time_ms = @intCast(std.time.milliTimestamp()),
|
||||||
|
.download_data = data,
|
||||||
|
};
|
||||||
|
try session.send(CmdID.CmdPlayerHeartBeatScRsp, rsp);
|
||||||
|
}
|
31
gameserver/src/services/misc.zig
Normal file
31
gameserver/src/services/misc.zig
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const protocol = @import("protocol");
|
||||||
|
const Session = @import("../Session.zig");
|
||||||
|
const Packet = @import("../Packet.zig");
|
||||||
|
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
const CmdID = protocol.CmdID;
|
||||||
|
|
||||||
|
pub fn onPlayerHeartBeat(session: *Session, packet: *const Packet, allocator: Allocator) !void {
|
||||||
|
const req = try packet.getProto(protocol.PlayerHeartBeatCsReq, allocator);
|
||||||
|
defer req.deinit();
|
||||||
|
|
||||||
|
const encoded = "ICAgICAgICBsb2NhbCBmdW5jdGlvbiBzZXRUZXh0Q29tcG9uZW50KHBhdGgsIG5ld1RleHQpDQogICAgICAgICAgICBsb2NhbCBvYmogPSBDUy5Vbml0eUVuZ2luZS5HYW1lT2JqZWN0LkZpbmQocGF0aCkNCiAgICAgICAgICAgIGlmIG9iaiB0aGVuDQogICAgICAgICAgICAgICAgbG9jYWwgdGV4dENvbXBvbmVudCA9IG9iajpHZXRDb21wb25lbnRJbkNoaWxkcmVuKHR5cGVvZihDUy5SUEcuQ2xpZW50LkxvY2FsaXplZFRleHQpKQ0KICAgICAgICAgICAgICAgIGlmIHRleHRDb21wb25lbnQgdGhlbg0KICAgICAgICAgICAgICAgICAgICB0ZXh0Q29tcG9uZW50LnRleHQgPSBuZXdUZXh0DQogICAgICAgICAgICAgICAgZW5kDQogICAgICAgICAgICBlbmQNCiAgICAgICAgZW5kDQogICAgICAgIA0KICAgICAgICBzZXRUZXh0Q29tcG9uZW50KCJVSVJvb3QvQWJvdmVEaWFsb2cvQmV0YUhpbnREaWFsb2coQ2xvbmUpIiwgIjxjb2xvcj0jMDNjMmZjPkNpcGhlclNSIGlzIGEgZnJlZSBhbmQgb3BlbiBzb3VyY2Ugc29mdHdhcmUuPC9jb2xvcj4iKQ0KICAgICAgICBzZXRUZXh0Q29tcG9uZW50KCJWZXJzaW9uVGV4dCIsICI8Y29sb3I9IzAzYzJmYz5WaXNpdCBkaXNjb3JkLmdnL3JldmVyc2Vkcm9vbXMgZm9yIG1vcmUgaW5mbyE8L2NvbG9yPiIpDQo=";
|
||||||
|
|
||||||
|
const dest_buf = try allocator.alloc(u8, try std.base64.standard.Decoder.calcSizeForSlice(encoded));
|
||||||
|
try std.base64.standard.Decoder.decode(dest_buf, encoded);
|
||||||
|
|
||||||
|
const managed_str = protocol.ManagedString.move(dest_buf, allocator);
|
||||||
|
const download_data = protocol.ClientDownloadData{
|
||||||
|
.version = 51,
|
||||||
|
.time = @intCast(std.time.milliTimestamp()),
|
||||||
|
.data = managed_str,
|
||||||
|
};
|
||||||
|
|
||||||
|
try session.send(CmdID.CmdPlayerHeartBeatScRsp, protocol.PlayerHeartBeatScRsp{
|
||||||
|
.retcode = 0,
|
||||||
|
.client_time_ms = req.client_time_ms,
|
||||||
|
.server_time_ms = @intCast(std.time.milliTimestamp()),
|
||||||
|
.download_data = download_data,
|
||||||
|
});
|
||||||
|
}
|
65
gameserver/src/services/mission.zig
Normal file
65
gameserver/src/services/mission.zig
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const protocol = @import("protocol");
|
||||||
|
const Session = @import("../Session.zig");
|
||||||
|
const Packet = @import("../Packet.zig");
|
||||||
|
const Data = @import("../data.zig");
|
||||||
|
|
||||||
|
const ArrayList = std.ArrayList;
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
const CmdID = protocol.CmdID;
|
||||||
|
|
||||||
|
pub fn onGetMissionStatus(session: *Session, packet: *const Packet, allocator: Allocator) !void {
|
||||||
|
const req = try packet.getProto(protocol.GetMissionStatusCsReq, allocator);
|
||||||
|
var rsp = protocol.GetMissionStatusScRsp.init(allocator);
|
||||||
|
|
||||||
|
rsp.retcode = 0;
|
||||||
|
for (req.sub_mission_id_list.items) |id| {
|
||||||
|
try rsp.SubMissionStatusList.append(protocol.Mission{ .id = id, .status = protocol.MissionStatus.MISSION_FINISH, .progress = 1 });
|
||||||
|
}
|
||||||
|
try rsp.FinishedMainMissionIdList.appendSlice(&Data.FinishedMainMissionIdList);
|
||||||
|
try rsp.CurversionFinishedMainMissionIdList.appendSlice(&Data.FinishedMainMissionIdList);
|
||||||
|
|
||||||
|
try session.send(CmdID.CmdGetMissionStatusScRsp, rsp);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn onGetTutorialGuideStatus(session: *Session, _: *const Packet, allocator: Allocator) !void {
|
||||||
|
var rsp = protocol.GetTutorialGuideScRsp.init(allocator);
|
||||||
|
|
||||||
|
rsp.retcode = 0;
|
||||||
|
for (Data.TutorialGuideIdList) |id| {
|
||||||
|
try rsp.TutorialGuideList.append(protocol.TutorialGuide{ .id = id, .Status = protocol.TutorialStatus.TUTORIAL_FINISH });
|
||||||
|
}
|
||||||
|
|
||||||
|
try session.send(CmdID.CmdGetTutorialGuideScRsp, rsp);
|
||||||
|
}
|
||||||
|
pub fn onFinishTutorialGuideStatus(session: *Session, _: *const Packet, allocator: Allocator) !void {
|
||||||
|
var rsp = protocol.FinishTutorialGuideScRsp.init(allocator);
|
||||||
|
|
||||||
|
rsp.retcode = 0;
|
||||||
|
for (Data.TutorialGuideIdList) |id| {
|
||||||
|
rsp.TutorialGuide = .{ .id = id, .Status = protocol.TutorialStatus.TUTORIAL_FINISH };
|
||||||
|
}
|
||||||
|
|
||||||
|
try session.send(CmdID.CmdFinishTutorialScRsp, rsp);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn onGetTutorialStatus(session: *Session, _: *const Packet, allocator: Allocator) !void {
|
||||||
|
var rsp = protocol.GetTutorialScRsp.init(allocator);
|
||||||
|
rsp.retcode = 0;
|
||||||
|
for (Data.FinishedTutorialIdList) |id| {
|
||||||
|
try rsp.TutorialList.append(protocol.Tutorial{ .id = id, .Status = protocol.TutorialStatus.TUTORIAL_FINISH });
|
||||||
|
}
|
||||||
|
|
||||||
|
try session.send(CmdID.CmdGetTutorialScRsp, rsp);
|
||||||
|
}
|
||||||
|
|
||||||
|
// added this to auto detect new tutorial guide id
|
||||||
|
pub fn onUnlockTutorialGuide(session: *Session, packet: *const Packet, allocator: Allocator) !void {
|
||||||
|
const req = try packet.getProto(protocol.UnlockTutorialGuideCsReq, allocator);
|
||||||
|
var rsp = protocol.UnlockTutorialGuideScRsp.init(allocator);
|
||||||
|
|
||||||
|
rsp.retcode = 0;
|
||||||
|
std.debug.print("UNLOCK TUTORIAL GUIDE ID: {}\n", .{req.group_id});
|
||||||
|
|
||||||
|
try session.send(CmdID.CmdUnlockTutorialGuideScRsp, rsp);
|
||||||
|
}
|
17
gameserver/src/services/multipath.zig
Normal file
17
gameserver/src/services/multipath.zig
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const protocol = @import("protocol");
|
||||||
|
const Session = @import("../Session.zig");
|
||||||
|
const Packet = @import("../Packet.zig");
|
||||||
|
const Config = @import("config.zig");
|
||||||
|
const Data = @import("../data.zig");
|
||||||
|
const MultiPathManager = @import("../manager/multipath_mgr.zig").MultiPathManager;
|
||||||
|
|
||||||
|
const ArrayList = std.ArrayList;
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
const CmdID = protocol.CmdID;
|
||||||
|
|
||||||
|
pub fn onGetMultiPathAvatarInfo(session: *Session, _: *const Packet, allocator: Allocator) !void {
|
||||||
|
var multipath = MultiPathManager.init(allocator);
|
||||||
|
const rsp = try multipath.createMultiPath(1100101);
|
||||||
|
try session.send(CmdID.CmdGetMultiPathAvatarInfoScRsp, rsp);
|
||||||
|
}
|
36
gameserver/src/services/pet.zig
Normal file
36
gameserver/src/services/pet.zig
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const protocol = @import("protocol");
|
||||||
|
const Session = @import("../Session.zig");
|
||||||
|
const Packet = @import("../Packet.zig");
|
||||||
|
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
const CmdID = protocol.CmdID;
|
||||||
|
|
||||||
|
const OwnedPet = [_]u32{ 251001, 251002 };
|
||||||
|
|
||||||
|
pub fn onGetPetData(session: *Session, _: *const Packet, allocator: Allocator) !void {
|
||||||
|
var rsp = protocol.GetPetDataScRsp.init(allocator);
|
||||||
|
|
||||||
|
rsp.retcode = 0;
|
||||||
|
rsp.cur_pet_id = 1002;
|
||||||
|
try rsp.unlocked_pet_id.appendSlice(&OwnedPet);
|
||||||
|
|
||||||
|
try session.send(CmdID.CmdGetPetDataScRsp, rsp);
|
||||||
|
}
|
||||||
|
pub fn onRecallPet(session: *Session, _: *const Packet, allocator: Allocator) !void {
|
||||||
|
var rsp = protocol.RecallPetScRsp.init(allocator);
|
||||||
|
|
||||||
|
rsp.retcode = 0;
|
||||||
|
rsp.cur_pet_id = 1002;
|
||||||
|
try session.send(CmdID.CmdRecallPetScRsp, rsp);
|
||||||
|
}
|
||||||
|
pub fn onSummonPet(session: *Session, _: *const Packet, allocator: Allocator) !void {
|
||||||
|
var rsp = protocol.CurPetChangedScNotify.init(allocator);
|
||||||
|
|
||||||
|
rsp.cur_pet_id = 1002;
|
||||||
|
try session.send(CmdID.CmdCurPetChangedScNotify, rsp);
|
||||||
|
try session.send(CmdID.CmdSummonPetScRsp, protocol.SummonPetScRsp{
|
||||||
|
.retcode = 0,
|
||||||
|
.cur_pet_id = rsp.cur_pet_id,
|
||||||
|
});
|
||||||
|
}
|
108
gameserver/src/services/profile.zig
Normal file
108
gameserver/src/services/profile.zig
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const protocol = @import("protocol");
|
||||||
|
const Session = @import("../Session.zig");
|
||||||
|
const Packet = @import("../Packet.zig");
|
||||||
|
const Data = @import("../data.zig");
|
||||||
|
|
||||||
|
const UidGenerator = @import("item.zig").UidGenerator;
|
||||||
|
const ArrayList = std.ArrayList;
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
const CmdID = protocol.CmdID;
|
||||||
|
|
||||||
|
// can change these id here for initial display
|
||||||
|
const SupportAvatar = [_]u32{
|
||||||
|
1407, 1403, 1402,
|
||||||
|
};
|
||||||
|
const ListAvatar = [_]u32{
|
||||||
|
1401, 1001, 1225, 1317, 1222,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn onGetPhoneData(session: *Session, _: *const Packet, allocator: Allocator) !void {
|
||||||
|
var rsp = protocol.GetPhoneDataScRsp.init(allocator);
|
||||||
|
rsp.retcode = 0;
|
||||||
|
rsp.cur_chat_bubble = 0;
|
||||||
|
rsp.cur_phone_theme = 0;
|
||||||
|
rsp.cur_phone_case = 254000;
|
||||||
|
try rsp.owned_chat_bubbles.appendSlice(&Data.OwnedChatBubbles);
|
||||||
|
try rsp.owned_phone_themes.appendSlice(&Data.OwnedPhoneThemes);
|
||||||
|
try rsp.owned_phone_cases.appendSlice(&Data.OwnedPhoneCases);
|
||||||
|
try session.send(CmdID.CmdGetPhoneDataScRsp, rsp);
|
||||||
|
}
|
||||||
|
pub fn onSelectPhoneTheme(session: *Session, packet: *const Packet, allocator: Allocator) !void {
|
||||||
|
const req = try packet.getProto(protocol.SelectPhoneThemeCsReq, allocator);
|
||||||
|
var rsp = protocol.SelectPhoneThemeScRsp.init(allocator);
|
||||||
|
rsp.retcode = 0;
|
||||||
|
rsp.cur_phone_theme = req.theme_id;
|
||||||
|
try session.send(CmdID.CmdSelectPhoneThemeScRsp, rsp);
|
||||||
|
}
|
||||||
|
pub fn onSelectChatBubble(session: *Session, packet: *const Packet, allocator: Allocator) !void {
|
||||||
|
const req = try packet.getProto(protocol.SelectChatBubbleCsReq, allocator);
|
||||||
|
var rsp = protocol.SelectChatBubbleScRsp.init(allocator);
|
||||||
|
rsp.retcode = 0;
|
||||||
|
rsp.cur_chat_bubble = req.bubble_id;
|
||||||
|
try session.send(CmdID.CmdSelectChatBubbleScRsp, rsp);
|
||||||
|
}
|
||||||
|
pub fn onGetPlayerBoardData(session: *Session, _: *const Packet, allocator: Allocator) !void {
|
||||||
|
var rsp = protocol.GetPlayerBoardDataScRsp.init(allocator);
|
||||||
|
var generator = UidGenerator().init();
|
||||||
|
var display_list = protocol.DisplayAvatarVec.init(allocator);
|
||||||
|
display_list.is_display = true;
|
||||||
|
rsp.retcode = 0;
|
||||||
|
rsp.OLDMJONBJOM = 253000;
|
||||||
|
rsp.signature = .{ .Const = "Visit discord.gg/reversedrooms for more info!" };
|
||||||
|
try rsp.assist_avatar_id_list.appendSlice(&SupportAvatar);
|
||||||
|
for (ListAvatar) |id| {
|
||||||
|
var A_list = protocol.DisplayAvatarData.init(allocator);
|
||||||
|
A_list.avatar_id = id;
|
||||||
|
A_list.pos = generator.nextId();
|
||||||
|
try display_list.display_avatar_list.append(A_list);
|
||||||
|
}
|
||||||
|
rsp.display_avatar_vec = display_list;
|
||||||
|
for (Data.OwnedHeadIcon) |head_id| {
|
||||||
|
const head_icon = protocol.HeadIconData{
|
||||||
|
.id = head_id,
|
||||||
|
};
|
||||||
|
try rsp.unlocked_head_icon_list.append(head_icon);
|
||||||
|
}
|
||||||
|
for (Data.OwnedPersonalCardSkin) |card_skin_id| {
|
||||||
|
try rsp.KKNJHENMGPK.append(card_skin_id);
|
||||||
|
}
|
||||||
|
try session.send(CmdID.CmdGetPlayerBoardDataScRsp, rsp);
|
||||||
|
}
|
||||||
|
pub fn onSetAssistAvatar(session: *Session, packet: *const Packet, allocator: Allocator) !void {
|
||||||
|
const req = try packet.getProto(protocol.SetAssistAvatarCsReq, allocator);
|
||||||
|
var rsp = protocol.SetAssistAvatarScRsp.init(allocator);
|
||||||
|
rsp.retcode = 0;
|
||||||
|
rsp.avatar_id = req.avatar_id;
|
||||||
|
rsp.avatar_id_list = req.avatar_id_list;
|
||||||
|
try session.send(CmdID.CmdSetAssistAvatarScRsp, rsp);
|
||||||
|
}
|
||||||
|
pub fn onSetDisplayAvatar(session: *Session, packet: *const Packet, allocator: Allocator) !void {
|
||||||
|
const req = try packet.getProto(protocol.SetDisplayAvatarCsReq, allocator);
|
||||||
|
var rsp = protocol.SetDisplayAvatarScRsp.init(allocator);
|
||||||
|
rsp.retcode = 0;
|
||||||
|
rsp.display_avatar_list = req.display_avatar_list;
|
||||||
|
try session.send(CmdID.CmdSetDisplayAvatarScRsp, rsp);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn onSetSignature(session: *Session, packet: *const Packet, allocator: Allocator) !void {
|
||||||
|
const req = try packet.getProto(protocol.SetSignatureCsReq, allocator);
|
||||||
|
var rsp = protocol.SetSignatureScRsp.init(allocator);
|
||||||
|
rsp.retcode = 0;
|
||||||
|
rsp.signature = req.signature;
|
||||||
|
try session.send(CmdID.CmdSetSignatureScRsp, rsp);
|
||||||
|
}
|
||||||
|
pub fn onSetGameplayBirthday(session: *Session, packet: *const Packet, allocator: Allocator) !void {
|
||||||
|
const req = try packet.getProto(protocol.SetGameplayBirthdayCsReq, allocator);
|
||||||
|
var rsp = protocol.SetGameplayBirthdayScRsp.init(allocator);
|
||||||
|
rsp.retcode = 0;
|
||||||
|
rsp.birthday = req.birthday;
|
||||||
|
try session.send(CmdID.CmdSetGameplayBirthdayScRsp, rsp);
|
||||||
|
}
|
||||||
|
pub fn onSetHeadIcon(session: *Session, packet: *const Packet, allocator: Allocator) !void {
|
||||||
|
const req = try packet.getProto(protocol.SetHeadIconCsReq, allocator);
|
||||||
|
var rsp = protocol.SetHeadIconScRsp.init(allocator);
|
||||||
|
rsp.retcode = 0;
|
||||||
|
rsp.current_head_icon_id = req.id;
|
||||||
|
try session.send(CmdID.CmdSetHeadIconScRsp, rsp);
|
||||||
|
}
|
146
gameserver/src/services/res_config.zig
Normal file
146
gameserver/src/services/res_config.zig
Normal file
|
@ -0,0 +1,146 @@
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
const ArrayList = std.ArrayList;
|
||||||
|
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
const ResConfig = struct {
|
||||||
|
planeID: u32,
|
||||||
|
props: ArrayList(Props),
|
||||||
|
monsters: ArrayList(Monsters),
|
||||||
|
teleports: ArrayList(Teleports),
|
||||||
|
};
|
||||||
|
const Vector = struct {
|
||||||
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
z: i32,
|
||||||
|
};
|
||||||
|
const Teleports = struct {
|
||||||
|
anchorId: u32,
|
||||||
|
groupId: u32,
|
||||||
|
instId: u32,
|
||||||
|
pos: Vector,
|
||||||
|
rot: Vector,
|
||||||
|
teleportId: u32,
|
||||||
|
};
|
||||||
|
const Monsters = struct {
|
||||||
|
groupId: u32,
|
||||||
|
instId: u32,
|
||||||
|
eventId: u32,
|
||||||
|
pos: Vector,
|
||||||
|
rot: Vector,
|
||||||
|
monsterId: u32,
|
||||||
|
};
|
||||||
|
const Props = struct {
|
||||||
|
groupId: u32,
|
||||||
|
instId: u32,
|
||||||
|
propState: u32,
|
||||||
|
pos: Vector,
|
||||||
|
rot: Vector,
|
||||||
|
propId: u32,
|
||||||
|
};
|
||||||
|
pub const SceneConfig = struct {
|
||||||
|
scene_config: ArrayList(ResConfig),
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn anchorLoader(allocator: Allocator, filename: []const u8) !SceneConfig {
|
||||||
|
const file = try std.fs.cwd().openFile(filename, .{});
|
||||||
|
defer file.close();
|
||||||
|
|
||||||
|
const file_size = try file.getEndPos();
|
||||||
|
const buffer = try file.readToEndAlloc(allocator, file_size);
|
||||||
|
defer allocator.free(buffer);
|
||||||
|
|
||||||
|
var json_tree = try std.json.parseFromSlice(std.json.Value, allocator, buffer, .{});
|
||||||
|
defer json_tree.deinit();
|
||||||
|
|
||||||
|
const root = json_tree.value;
|
||||||
|
const config: SceneConfig = try parseAnchor(root, allocator);
|
||||||
|
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parseAnchor(root: anytype, allocator: Allocator) !SceneConfig {
|
||||||
|
var res_config = ArrayList(ResConfig).init(allocator);
|
||||||
|
for (root.object.get("scene_config").?.array.items) |res_json| {
|
||||||
|
var res = ResConfig{
|
||||||
|
.planeID = @intCast(res_json.object.get("planeID").?.integer),
|
||||||
|
.props = ArrayList(Props).init(allocator),
|
||||||
|
.monsters = ArrayList(Monsters).init(allocator),
|
||||||
|
.teleports = ArrayList(Teleports).init(allocator),
|
||||||
|
};
|
||||||
|
for (res_json.object.get("props").?.array.items) |scene_prop| {
|
||||||
|
var prop = Props{
|
||||||
|
.groupId = @intCast(scene_prop.object.get("groupId").?.integer),
|
||||||
|
.instId = @intCast(scene_prop.object.get("instId").?.integer),
|
||||||
|
.propState = @intCast(scene_prop.object.get("propState").?.integer),
|
||||||
|
.pos = undefined,
|
||||||
|
.rot = undefined,
|
||||||
|
.propId = @intCast(scene_prop.object.get("propId").?.integer),
|
||||||
|
};
|
||||||
|
const pos_json = scene_prop.object.get("pos").?;
|
||||||
|
prop.pos = Vector{
|
||||||
|
.x = @intCast(pos_json.object.get("x").?.integer),
|
||||||
|
.y = @intCast(pos_json.object.get("y").?.integer),
|
||||||
|
.z = @intCast(pos_json.object.get("z").?.integer),
|
||||||
|
};
|
||||||
|
const rot_json = scene_prop.object.get("rot").?;
|
||||||
|
prop.rot = Vector{
|
||||||
|
.x = @intCast(rot_json.object.get("x").?.integer),
|
||||||
|
.y = @intCast(rot_json.object.get("y").?.integer),
|
||||||
|
.z = @intCast(rot_json.object.get("z").?.integer),
|
||||||
|
};
|
||||||
|
try res.props.append(prop);
|
||||||
|
}
|
||||||
|
for (res_json.object.get("monsters").?.array.items) |monster_json| {
|
||||||
|
var monster = Monsters{
|
||||||
|
.groupId = @intCast(monster_json.object.get("groupId").?.integer),
|
||||||
|
.instId = @intCast(monster_json.object.get("instId").?.integer),
|
||||||
|
.eventId = @intCast(monster_json.object.get("eventId").?.integer),
|
||||||
|
.monsterId = @intCast(monster_json.object.get("monsterId").?.integer),
|
||||||
|
.pos = undefined,
|
||||||
|
.rot = undefined,
|
||||||
|
};
|
||||||
|
const pos_json = monster_json.object.get("pos").?;
|
||||||
|
monster.pos = Vector{
|
||||||
|
.x = @intCast(pos_json.object.get("x").?.integer),
|
||||||
|
.y = @intCast(pos_json.object.get("y").?.integer),
|
||||||
|
.z = @intCast(pos_json.object.get("z").?.integer),
|
||||||
|
};
|
||||||
|
const rot_json = monster_json.object.get("rot").?;
|
||||||
|
monster.rot = Vector{
|
||||||
|
.x = @intCast(rot_json.object.get("x").?.integer),
|
||||||
|
.y = @intCast(rot_json.object.get("y").?.integer),
|
||||||
|
.z = @intCast(rot_json.object.get("z").?.integer),
|
||||||
|
};
|
||||||
|
try res.monsters.append(monster);
|
||||||
|
}
|
||||||
|
for (res_json.object.get("teleports").?.array.items) |teleport_json| {
|
||||||
|
var teleport = Teleports{
|
||||||
|
.anchorId = @intCast(teleport_json.object.get("anchorId").?.integer),
|
||||||
|
.groupId = @intCast(teleport_json.object.get("groupId").?.integer),
|
||||||
|
.instId = @intCast(teleport_json.object.get("instId").?.integer),
|
||||||
|
.teleportId = @intCast(teleport_json.object.get("teleportId").?.integer),
|
||||||
|
.pos = undefined,
|
||||||
|
.rot = undefined,
|
||||||
|
};
|
||||||
|
const pos_json = teleport_json.object.get("pos").?;
|
||||||
|
teleport.pos = Vector{
|
||||||
|
.x = @intCast(pos_json.object.get("x").?.integer),
|
||||||
|
.y = @intCast(pos_json.object.get("y").?.integer),
|
||||||
|
.z = @intCast(pos_json.object.get("z").?.integer),
|
||||||
|
};
|
||||||
|
const rot_json = teleport_json.object.get("rot").?;
|
||||||
|
teleport.rot = Vector{
|
||||||
|
.x = @intCast(rot_json.object.get("x").?.integer),
|
||||||
|
.y = @intCast(rot_json.object.get("y").?.integer),
|
||||||
|
.z = @intCast(rot_json.object.get("z").?.integer),
|
||||||
|
};
|
||||||
|
try res.teleports.append(teleport);
|
||||||
|
}
|
||||||
|
try res_config.append(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
return SceneConfig{
|
||||||
|
.scene_config = res_config,
|
||||||
|
};
|
||||||
|
}
|
235
gameserver/src/services/scene.zig
Normal file
235
gameserver/src/services/scene.zig
Normal file
|
@ -0,0 +1,235 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const protocol = @import("protocol");
|
||||||
|
const Session = @import("../Session.zig");
|
||||||
|
const Packet = @import("../Packet.zig");
|
||||||
|
const Config = @import("config.zig");
|
||||||
|
const Data = @import("../data.zig");
|
||||||
|
const Res_config = @import("res_config.zig");
|
||||||
|
const LineupManager = @import("../manager/lineup_mgr.zig").LineupManager;
|
||||||
|
const SceneManager = @import("../manager/scene_mgr.zig").SceneManager;
|
||||||
|
|
||||||
|
const ArrayList = std.ArrayList;
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
const CmdID = protocol.CmdID;
|
||||||
|
|
||||||
|
const log = std.log.scoped(.scene_service);
|
||||||
|
|
||||||
|
pub fn onGetCurSceneInfo(session: *Session, _: *const Packet, allocator: Allocator) !void {
|
||||||
|
var scene_manager = SceneManager.init(allocator);
|
||||||
|
const scene_info = try scene_manager.createScene(20432, 20432001, 2043201, 1213);
|
||||||
|
try session.send(CmdID.CmdGetCurSceneInfoScRsp, protocol.GetCurSceneInfoScRsp{
|
||||||
|
.scene = scene_info,
|
||||||
|
.retcode = 0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
pub fn onSceneEntityMove(session: *Session, packet: *const Packet, allocator: Allocator) !void {
|
||||||
|
const req = try packet.getProto(protocol.SceneEntityMoveCsReq, allocator);
|
||||||
|
for (req.entity_motion_list.items) |entity_motion| {
|
||||||
|
if (entity_motion.motion) |motion| {
|
||||||
|
if (entity_motion.entity_id > 99999 or entity_motion.entity_id == 0)
|
||||||
|
log.debug("[POSITION] entity_id: {}, motion: {}", .{ entity_motion.entity_id, motion });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try session.send(CmdID.CmdSceneEntityMoveScRsp, protocol.SceneEntityMoveScRsp{
|
||||||
|
.retcode = 0,
|
||||||
|
.entity_motion_list = req.entity_motion_list,
|
||||||
|
.download_data = null,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn onEnterScene(session: *Session, packet: *const Packet, allocator: Allocator) !void {
|
||||||
|
const entrance_config = try Config.loadMapEntranceConfig(allocator, "resources/MapEntrance.json");
|
||||||
|
const req = try packet.getProto(protocol.EnterSceneCsReq, allocator);
|
||||||
|
var lineup_mgr = LineupManager.init(allocator);
|
||||||
|
const lineup = try lineup_mgr.createLineup();
|
||||||
|
var scene_manager = SceneManager.init(allocator);
|
||||||
|
var floorID: u32 = 0;
|
||||||
|
var planeID: u32 = 0;
|
||||||
|
var teleportID: u32 = 0;
|
||||||
|
for (entrance_config.map_entrance_config.items) |entrConf| {
|
||||||
|
if (entrConf.id == req.entry_id) {
|
||||||
|
floorID = entrConf.floor_id;
|
||||||
|
planeID = entrConf.plane_id;
|
||||||
|
teleportID = req.teleport_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const scene_info = try scene_manager.createScene(planeID, floorID, req.entry_id, teleportID);
|
||||||
|
std.debug.print("ENTER SCENE ENTRY ID: {}, PLANE ID: {}, FLOOR ID: {}, TELEPORT ID: {}\n", .{ req.entry_id, planeID, floorID, teleportID });
|
||||||
|
try session.send(CmdID.CmdEnterSceneByServerScNotify, protocol.EnterSceneByServerScNotify{
|
||||||
|
.lineup = lineup,
|
||||||
|
.reason = protocol.EnterSceneReason.ENTER_SCENE_REASON_NONE,
|
||||||
|
.scene = scene_info,
|
||||||
|
});
|
||||||
|
try session.send(CmdID.CmdEnterSceneScRsp, protocol.EnterSceneScRsp{
|
||||||
|
.retcode = 0,
|
||||||
|
.game_story_line_id = req.game_story_line_id,
|
||||||
|
.is_close_map = req.is_close_map,
|
||||||
|
.content_id = req.content_id,
|
||||||
|
.is_over_map = true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO FIX CURRENT SCENE MAP DISPLAY
|
||||||
|
pub fn onGetSceneMapInfo(session: *Session, packet: *const Packet, allocator: Allocator) !void {
|
||||||
|
const req = try packet.getProto(protocol.GetSceneMapInfoCsReq, allocator);
|
||||||
|
const res_config = try Res_config.anchorLoader(allocator, "resources/res.json");
|
||||||
|
const ranges = [_][2]usize{
|
||||||
|
.{ 0, 101 },
|
||||||
|
.{ 10000, 10051 },
|
||||||
|
.{ 20000, 20001 },
|
||||||
|
.{ 30000, 30020 },
|
||||||
|
};
|
||||||
|
const chest_list = &[_]protocol.ChestInfo{
|
||||||
|
.{ .chest_type = protocol.ChestType.MAP_INFO_CHEST_TYPE_NORMAL },
|
||||||
|
.{ .chest_type = protocol.ChestType.MAP_INFO_CHEST_TYPE_CHALLENGE },
|
||||||
|
.{ .chest_type = protocol.ChestType.MAP_INFO_CHEST_TYPE_PUZZLE },
|
||||||
|
};
|
||||||
|
std.debug.print("SCENE MAP FLOOR ID REQ: {}\n", .{req.floor_id_list});
|
||||||
|
for (req.floor_id_list.items) |floor_id| {
|
||||||
|
var rsp = protocol.GetSceneMapInfoScRsp.init(allocator);
|
||||||
|
rsp.retcode = 0;
|
||||||
|
rsp.content_id = req.content_id;
|
||||||
|
rsp.entry_story_line_id = req.entry_story_line_id;
|
||||||
|
var map_info = protocol.SceneMapInfo.init(allocator);
|
||||||
|
try map_info.chest_list.appendSlice(chest_list);
|
||||||
|
map_info.entry_id = @intCast(floor_id);
|
||||||
|
map_info.floor_id = @intCast(floor_id);
|
||||||
|
map_info.cur_map_entry_id = @intCast(floor_id);
|
||||||
|
for (res_config.scene_config.items) |sceneConf| {
|
||||||
|
if (sceneConf.planeID != floor_id / 1000) continue;
|
||||||
|
try map_info.unlock_teleport_list.ensureUnusedCapacity(sceneConf.teleports.items.len);
|
||||||
|
try map_info.maze_prop_list.ensureUnusedCapacity(sceneConf.props.items.len);
|
||||||
|
try map_info.maze_group_list.ensureUnusedCapacity(sceneConf.props.items.len);
|
||||||
|
for (ranges) |range| {
|
||||||
|
for (range[0]..range[1]) |i| {
|
||||||
|
try map_info.lighten_section_list.append(@intCast(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (sceneConf.teleports.items) |teleConf| {
|
||||||
|
try map_info.unlock_teleport_list.append(@intCast(teleConf.teleportId));
|
||||||
|
}
|
||||||
|
for (sceneConf.props.items) |propConf| {
|
||||||
|
try map_info.maze_prop_list.append(protocol.MazePropState{
|
||||||
|
.group_id = propConf.groupId,
|
||||||
|
.config_id = propConf.instId,
|
||||||
|
.state = propConf.propState,
|
||||||
|
});
|
||||||
|
try map_info.LMNGAHFNAON.append(protocol.OFCAIGDHPOH{
|
||||||
|
.group_id = propConf.groupId,
|
||||||
|
.config_id = propConf.instId,
|
||||||
|
.state = propConf.propState,
|
||||||
|
});
|
||||||
|
try map_info.maze_group_list.append(protocol.MazeGroup{
|
||||||
|
.NOBKEONAKLE = std.ArrayList(u32).init(allocator),
|
||||||
|
.group_id = propConf.groupId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try rsp.scene_map_info.append(map_info);
|
||||||
|
try session.send(CmdID.CmdGetSceneMapInfoScRsp, rsp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn onGetUnlockTeleport(session: *Session, packet: *const Packet, allocator: Allocator) !void {
|
||||||
|
const req = try packet.getProto(protocol.GetUnlockTeleportCsReq, allocator);
|
||||||
|
std.debug.print("UNLOCK TELEPORT ENTRY ID REQ: {}\n", .{req.entry_id_list});
|
||||||
|
var rsp = protocol.GetUnlockTeleportScRsp.init(allocator);
|
||||||
|
const res_config = try Res_config.anchorLoader(allocator, "resources/res.json");
|
||||||
|
for (res_config.scene_config.items) |sceneCof| {
|
||||||
|
for (sceneCof.teleports.items) |tp| {
|
||||||
|
try rsp.unlock_teleport_list.append(tp.teleportId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rsp.retcode = 0;
|
||||||
|
try session.send(CmdID.CmdGetUnlockTeleportScRsp, rsp);
|
||||||
|
}
|
||||||
|
pub fn onEnterSection(session: *Session, packet: *const Packet, allocator: Allocator) !void {
|
||||||
|
const req = try packet.getProto(protocol.EnterSectionCsReq, allocator);
|
||||||
|
var rsp = protocol.EnterSectionScRsp.init(allocator);
|
||||||
|
rsp.retcode = 0;
|
||||||
|
std.debug.print("ENTER SECTION Id: {}\n", .{req.section_id});
|
||||||
|
try session.send(CmdID.CmdEnterSectionScRsp, rsp);
|
||||||
|
}
|
||||||
|
pub fn onSceneEntityTeleport(session: *Session, packet: *const Packet, allocator: Allocator) !void {
|
||||||
|
const req = try packet.getProto(protocol.SceneEntityTeleportCsReq, allocator);
|
||||||
|
var rsp = protocol.SceneEntityTeleportScRsp.init(allocator);
|
||||||
|
rsp.retcode = 0;
|
||||||
|
rsp.entity_motion = req.entity_motion;
|
||||||
|
std.debug.print("SCENE ENTITY TP ENTRY ID: {}\n", .{req.entry_id});
|
||||||
|
try session.send(CmdID.CmdSceneEntityTeleportScRsp, rsp);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn onGetFirstTalkNpc(session: *Session, packet: *const Packet, allocator: Allocator) !void {
|
||||||
|
const req = try packet.getProto(protocol.GetFirstTalkNpcCsReq, allocator);
|
||||||
|
var rsp = protocol.GetFirstTalkNpcScRsp.init(allocator);
|
||||||
|
rsp.retcode = 0;
|
||||||
|
for (req.npc_id_list.items) |id| {
|
||||||
|
try rsp.npc_meet_status_list.append(protocol.FirstNpcTalkInfo{ .npc_id = id, .is_meet = true });
|
||||||
|
}
|
||||||
|
try session.send(CmdID.CmdGetFirstTalkNpcScRsp, rsp);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn onGetFirstTalkByPerformanceNp(session: *Session, packet: *const Packet, allocator: Allocator) !void {
|
||||||
|
const req = try packet.getProto(protocol.GetFirstTalkByPerformanceNpcCsReq, allocator);
|
||||||
|
var rsp = protocol.GetFirstTalkByPerformanceNpcScRsp.init(allocator);
|
||||||
|
rsp.retcode = 0;
|
||||||
|
for (req.performance_id_list.items) |id| {
|
||||||
|
try rsp.npc_meet_status_list.append(
|
||||||
|
protocol.NpcMeetByPerformanceStatus{ .performance_id = id, .is_meet = true },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
try session.send(CmdID.CmdGetFirstTalkByPerformanceNpcScRsp, rsp);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn onGetNpcTakenReward(session: *Session, packet: *const Packet, allocator: Allocator) !void {
|
||||||
|
const req = try packet.getProto(protocol.GetNpcTakenRewardCsReq, allocator);
|
||||||
|
var rsp = protocol.GetNpcTakenRewardScRsp.init(allocator);
|
||||||
|
const EventList = [_]u32{ 2136, 2134 };
|
||||||
|
rsp.retcode = 0;
|
||||||
|
rsp.npc_id = req.npc_id;
|
||||||
|
try rsp.talk_event_list.appendSlice(&EventList);
|
||||||
|
try session.send(CmdID.CmdGetNpcTakenRewardScRsp, rsp);
|
||||||
|
}
|
||||||
|
pub fn onUpdateGroupProperty(session: *Session, packet: *const Packet, allocator: Allocator) !void {
|
||||||
|
const req = try packet.getProto(protocol.UpdateGroupPropertyCsReq, allocator);
|
||||||
|
var rsp = protocol.UpdateGroupPropertyScRsp.init(allocator);
|
||||||
|
rsp.retcode = 0;
|
||||||
|
rsp.floor_id = req.floor_id;
|
||||||
|
rsp.group_id = req.group_id;
|
||||||
|
rsp.dimension_id = req.dimension_id;
|
||||||
|
rsp.JAIBIEEKHEG = req.JAIBIEEKHEG;
|
||||||
|
try session.send(CmdID.CmdUpdateGroupPropertyScRsp, rsp);
|
||||||
|
}
|
||||||
|
pub fn onChangePropTimeline(session: *Session, packet: *const Packet, allocator: Allocator) !void {
|
||||||
|
const req = try packet.getProto(protocol.ChangePropTimelineInfoCsReq, allocator);
|
||||||
|
try session.send(CmdID.CmdChangePropTimelineInfoScRsp, protocol.ChangePropTimelineInfoScRsp{
|
||||||
|
.retcode = 0,
|
||||||
|
.prop_entity_id = req.prop_entity_id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
pub fn onDeactivateFarmElement(session: *Session, packet: *const Packet, allocator: Allocator) !void {
|
||||||
|
const req = try packet.getProto(protocol.DeactivateFarmElementCsReq, allocator);
|
||||||
|
std.debug.print("DEACTIVATE FARM ELEMENT ENTITY ID: {}\n", .{req.entity_id});
|
||||||
|
try session.send(CmdID.CmdDeactivateFarmElementScRsp, protocol.DeactivateFarmElementScRsp{
|
||||||
|
.retcode = 0,
|
||||||
|
.entity_id = req.entity_id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
pub fn onSetGroupCustomSaveData(session: *Session, packet: *const Packet, allocator: Allocator) !void {
|
||||||
|
const req = try packet.getProto(protocol.SetGroupCustomSaveDataCsReq, allocator);
|
||||||
|
switch (req.AAMHHECOCOI) {
|
||||||
|
.Owned => |val| {
|
||||||
|
std.debug.print("CUSTOM SAVE DATA REQ: {s}\n", .{val.str});
|
||||||
|
},
|
||||||
|
.Const => |val| {
|
||||||
|
std.debug.print("CUSTOM SAVE DATA REQ: {s}\n", .{val});
|
||||||
|
},
|
||||||
|
.Empty => {
|
||||||
|
std.debug.print("CUSTOM SAVE DATA REQ: <empty string>\n", .{});
|
||||||
|
},
|
||||||
|
}
|
||||||
|
try session.send(CmdID.CmdSetGroupCustomSaveDataScRsp, protocol.SetGroupCustomSaveDataScRsp{
|
||||||
|
.retcode = 0,
|
||||||
|
.group_id = req.group_id,
|
||||||
|
.entry_id = req.entry_id,
|
||||||
|
});
|
||||||
|
}
|
18
hotfix.json
Normal file
18
hotfix.json
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
{
|
||||||
|
"OSBETAWin3.2.51": {
|
||||||
|
"asset_bundle_url": "https://autopatchos.starrails.com/asb/BetaLive/output_9961450_404865fc6856_fbf8b526e6b08c",
|
||||||
|
"ex_resource_url": "https://autopatchos.starrails.com/design_data/BetaLive/output_9984498_2b39fa0086e4_1f081bc40d1782",
|
||||||
|
"ifix_url": "https://autopatchos.starrails.com/ifix/BetaLive/output_0_40d2ce0253",
|
||||||
|
"ifix_version": "0",
|
||||||
|
"lua_url": "https://autopatchos.starrails.com/lua/BetaLive/output_9961575_227d4559d8e3_cd27ed7ca2bbfd",
|
||||||
|
"lua_version": ""
|
||||||
|
},
|
||||||
|
"OSBETAWin3.2.52": {
|
||||||
|
"asset_bundle_url": "https://autopatchos.starrails.com/asb/BetaLive/output_10025177_f10d2880371c_719811bc00424c",
|
||||||
|
"ex_resource_url": "https://autopatchos.starrails.com/design_data/BetaLive/output_10037027_be14d3e01696_5aa1cd3547cbdb",
|
||||||
|
"ifix_url": "https://autopatchos.starrails.com/ifix/BetaLive/output_0_40d2ce0253_6d871f8bca6eb4",
|
||||||
|
"ifix_version": "0",
|
||||||
|
"lua_url": "https://autopatchos.starrails.com/lua/BetaLive/output_10025298_1dc728a6c8ee_1128f068b506d4",
|
||||||
|
"lua_version": ""
|
||||||
|
}
|
||||||
|
}
|
18
protocol/build.zig
Normal file
18
protocol/build.zig
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const protobuf = @import("protobuf");
|
||||||
|
|
||||||
|
pub fn build(b: *std.Build) void {
|
||||||
|
const target = b.standardTargetOptions(.{});
|
||||||
|
const optimize = b.standardOptimizeOption(.{});
|
||||||
|
|
||||||
|
const protobuf_dep = b.dependency("protobuf", .{
|
||||||
|
.optimize = optimize,
|
||||||
|
.target = target,
|
||||||
|
});
|
||||||
|
|
||||||
|
const protocol = b.addModule("protocol", .{
|
||||||
|
.root_source_file = b.path("src/root.zig"),
|
||||||
|
});
|
||||||
|
//
|
||||||
|
protocol.addImport("protobuf", protobuf_dep.module("protobuf"));
|
||||||
|
}
|
17
protocol/build.zig.zon
Normal file
17
protocol/build.zig.zon
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
.{
|
||||||
|
.name = "protocol",
|
||||||
|
.version = "0.0.0",
|
||||||
|
.dependencies = .{
|
||||||
|
.protobuf = .{
|
||||||
|
//.url = "https://github.com/Arwalk/zig-protobuf/archive/7c49ed66e029c9c7e6253b3d6d256118745550a4.tar.gz",
|
||||||
|
|
||||||
|
.url = "git+https://github.com/Arwalk/zig-protobuf?ref=v2.0.0#42ccf142e5d6b7de886b766af7d287021c561dfd",
|
||||||
|
.hash = "122063ee7ff32a3c1aefd91a46a9fc23df0571949c3a02e2f44d39afbad0b53018a3",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
.paths = .{
|
||||||
|
"build.zig",
|
||||||
|
"build.zig.zon",
|
||||||
|
"src",
|
||||||
|
},
|
||||||
|
}
|
49028
protocol/src/protocol.pb.zig
Normal file
49028
protocol/src/protocol.pb.zig
Normal file
File diff suppressed because it is too large
Load diff
1894
protocol/src/root.zig
Normal file
1894
protocol/src/root.zig
Normal file
File diff suppressed because it is too large
Load diff
30860
resources/ChallengeMazeConfig.json
Normal file
30860
resources/ChallengeMazeConfig.json
Normal file
File diff suppressed because it is too large
Load diff
6685
resources/MapEntrance.json
Normal file
6685
resources/MapEntrance.json
Normal file
File diff suppressed because it is too large
Load diff
4277
resources/MazePlane.json
Normal file
4277
resources/MazePlane.json
Normal file
File diff suppressed because it is too large
Load diff
466085
resources/res.json
Normal file
466085
resources/res.json
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue