new file: build.zig

new file:   build.zig.zon
	new file:   config.json
	new file:   dispatch/build.zig
	new file:   dispatch/build.zig.zon
	new file:   dispatch/src/authentication.zig
	new file:   dispatch/src/dispatch.zig
	new file:   dispatch/src/main.zig
	new file:   gameserver/build.zig
	new file:   gameserver/build.zig.zon
	new file:   gameserver/src/Packet.zig
	new file:   gameserver/src/Session.zig
	new file:   gameserver/src/api.zig
	new file:   gameserver/src/command.zig
	new file:   gameserver/src/commands/help.zig
	new file:   gameserver/src/commands/refill.zig
	new file:   gameserver/src/commands/sync.zig
	new file:   gameserver/src/commands/test.zig
	new file:   gameserver/src/commands/tp.zig
	new file:   gameserver/src/commands/unstuck.zig
	new file:   gameserver/src/data.zig
	new file:   gameserver/src/handlers.zig
	new file:   gameserver/src/main.zig
	new file:   gameserver/src/manager/battle_mgr.zig
	new file:   gameserver/src/manager/lineup_mgr.zig
	new file:   gameserver/src/manager/multipath_mgr.zig
	new file:   gameserver/src/manager/scene_mgr.zig
	new file:   gameserver/src/network.zig
	new file:   gameserver/src/services/avatar.zig
	new file:   gameserver/src/services/battle.zig
	new file:   gameserver/src/services/challenge.zig
	new file:   gameserver/src/services/chat.zig
	new file:   gameserver/src/services/config.zig
	new file:   gameserver/src/services/events.zig
	new file:   gameserver/src/services/gacha.zig
	new file:   gameserver/src/services/item.zig
	new file:   gameserver/src/services/lineup.zig
	new file:   gameserver/src/services/login.zig
	new file:   gameserver/src/services/mail.zig
	new file:   gameserver/src/services/misc.zig
	new file:   gameserver/src/services/mission.zig
	new file:   gameserver/src/services/multipath.zig
	new file:   gameserver/src/services/pet.zig
	new file:   gameserver/src/services/profile.zig
	new file:   gameserver/src/services/res_config.zig
	new file:   gameserver/src/services/scene.zig
	new file:   protocol/build.zig
	new file:   protocol/build.zig.zon
	new file:   protocol/src/protocol.pb.zig
	new file:   protocol/src/root.zig
	new file:   resources/ChallengeMazeConfig.json
	new file:   resources/MapEntrance.json
	new file:   resources/MazePlane.json
	new file:   resources/res.json
This commit is contained in:
HuLiNap 2025-03-19 22:01:41 +07:00
parent 8a5178b2fc
commit cd8c2843bd
54 changed files with 514729 additions and 0 deletions

64
build.zig Normal file
View 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
View file

@ -0,0 +1,15 @@
.{
.name = "YunliSR",
.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 = .{""},
}

43
config.json Normal file
View file

@ -0,0 +1,43 @@
{
"avatar_config": [
{
"name": "Castorice",
"id": 1407,
"hp": 100,
"sp": 50,
"level": 80,
"promotion": 6,
"rank": 6,
"lightcone": {
"id": 23040,
"rank": 5,
"level": 80,
"promotion": 6
},
"relics": [
"61241,15,1,4,8:2:4,11:2:0,4:2:2,9:3:6",
"61242,15,1,4,8:3:6,9:3:0,4:2:0,11:1:0",
"61243,15,5,4,8:3:3,4:4:8,11:1:1,1:1:2",
"61244,15,1,4,9:3:0,1:2:4,8:3:0,11:1:1",
"63195,15,9,4,9:3:6,8:3:0,4:2:4,11:1:0",
"63196,15,3,4,1:2:4,11:2:0,9:3:6,8:2:0"
],
"use_technique": true
}
],
"battle_config": {
"battle_id": 1,
"stage_id": 420244,
"cycle_count": 30,
"monster_wave": [
[
203401404
]
],
"monster_level": 90,
"blessings": [
3111051,
3110008
]
}
}

41
dispatch/build.zig Normal file
View 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);
}

32
dispatch/build.zig.zon Normal file
View file

@ -0,0 +1,32 @@
.{
.name = "dispatch",
.version = "0.0.1",
.minimum_zig_version = "0.13.0",
// This field is optional.
// Each dependency must either provide a `url` and `hash`, or a `path`.
// `zig build --fetch` can be used to fetch all dependencies of a package, recursively.
// Once all dependencies are fetched, `zig build` no longer requires
// internet connectivity.
.dependencies = .{
.httpz = .{
.url = "https://github.com/karlseguin/http.zig/archive/406fb00f8c43fe9b2da0f32f428675755c67d054.tar.gz",
.hash = "12209e87663712928c6ae1c690e65df15a796e970e5d18f5e4e05f0eb10404883d23",
},
.protocol = .{
.path = "../protocol",
},
.tls12 = .{
.url = "https://github.com/melonedo/zig-tls12/archive/refs/heads/main.zip",
.hash = "12202bb2c3824deecd4ccd666ca3dcb9c92c2c68698afdb92b382c0f5cb1b86da8bc",
},
},
.paths = .{
"build.zig",
"build.zig.zon",
"src",
// For example...
//"LICENSE",
//"README.md",
},
}

View 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 = .{
.arean_code = "/hkrpg_global/mdk/shield/api/login",
.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",
},
},
}, .{});
}

138
dispatch/src/dispatch.zig Normal file
View file

@ -0,0 +1,138 @@
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 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";
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 = "CastoriceSR" },
.display_name = .{ .Const = "CastoriceSR" },
.env_type = .{ .Const = "21" },
.title = .{ .Const = "CastoriceSR" },
.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 "";
const dispatch_seed = query.get("dispatch_seed") orelse "";
const host = selectHost(version);
const gatewayUrl = constructUrl(host, version, dispatch_seed);
var assetBundleUrl: []const u8 = undefined;
var exResourceUrl: []const u8 = undefined;
var luaUrl: []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);
assetBundleUrl = gateserver_proto.asset_bundle_url.Owned.str;
exResourceUrl = gateserver_proto.ex_resource_url.Owned.str;
luaUrl = gateserver_proto.lua_url.Owned.str;
std.log.info("Get Version >> {s}", .{version});
std.log.info("Get AssetBundleUrl >> {s}", .{assetBundleUrl});
std.log.info("Get ExResourceUrl >> {s}", .{exResourceUrl});
std.log.info("Get LuaUrl >> {s}", .{luaUrl});
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;
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 {
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 "";
}

30
dispatch/src/main.zig Normal file
View file

@ -0,0 +1,30 @@
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");
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 = 21000 });
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);
try server.listen();
}

28
gameserver/build.zig Normal file
View 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
View 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
View 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);
}

View 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});
}

86
gameserver/src/api.zig Normal file
View file

@ -0,0 +1,86 @@
const std = @import("std");
const json = std.json;
const Allocator = std.mem.Allocator;
pub const Character = struct {
rank: []const u8,
};
pub const Lightcone = struct {
rank: []const u8,
};
pub fn fetchDataFromApi(allocator: Allocator, url: []const u8) ![]u8 {
const child = try std.process.Child.run(.{
.allocator = allocator,
.argv = &[_][]const u8{ "curl", "-s", url },
.max_output_bytes = 10 * 1024 * 1024,
});
return child.stdout;
}
pub fn fetchDataMap(
comptime T: type,
allocator: Allocator,
json_data: []const u8,
filterFn: *const fn (u32) bool,
) !std.AutoHashMap(u32, T) {
var map = std.AutoHashMap(u32, T).init(allocator);
errdefer map.deinit();
var parsed = try json.parseFromSlice(std.json.Value, allocator, json_data, .{});
defer parsed.deinit();
const root = parsed.value.object;
var iter = root.iterator();
while (iter.next()) |entry| {
const id = std.fmt.parseInt(u32, entry.key_ptr.*, 10) catch continue;
const obj = entry.value_ptr.*.object;
if (!filterFn(id)) continue;
const rank_value = obj.get("rank") orelse continue;
const rank_str = if (rank_value == .string) rank_value.string else continue;
try map.put(id, T{
.rank = rank_str,
});
}
return map;
}
fn filterCharacterID(id: u32) bool {
return id <= 8001 and id != 1224;
}
fn filterLightconeID(id: u32) bool {
return id >= 20000;
}
pub fn fetchAvatarID(allocator: Allocator) !std.AutoHashMap(u32, Character) {
const json_data = try fetchDataFromApi(allocator, "https://api.hakush.in/hsr/data/character.json");
defer allocator.free(json_data);
return fetchDataMap(Character, allocator, json_data, filterCharacterID);
}
pub fn fetchLightconeID(allocator: Allocator) !std.AutoHashMap(u32, Lightcone) {
const json_data = try fetchDataFromApi(allocator, "https://api.hakush.in/hsr/data/lightcone.json");
defer allocator.free(json_data);
return fetchDataMap(Lightcone, allocator, json_data, filterLightconeID);
}
pub fn AvatarList(allocator: Allocator, rank_filter: ?[]const u8) !std.ArrayList(u32) {
return filterIDList(Character, allocator, try fetchAvatarID(allocator), rank_filter);
}
pub fn LightconeList(allocator: Allocator, rank_filter: ?[]const u8) !std.ArrayList(u32) {
return filterIDList(Lightcone, allocator, try fetchLightconeID(allocator), rank_filter);
}
fn filterIDList(comptime T: type, allocator: Allocator, map: std.AutoHashMap(u32, T), rank_filter: ?[]const u8) !std.ArrayList(u32) {
var id_list = std.ArrayList(u32).init(allocator);
errdefer id_list.deinit();
var iter = map.iterator();
while (iter.next()) |entry| {
const id = entry.key_ptr.*;
const item = entry.value_ptr.*;
if (rank_filter) |rank| {
if (!std.mem.eql(u8, item.rank, rank)) continue;
}
try id_list.append(id);
}
return id_list;
}

View 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 test_command = @import("./commands/test.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 = test_command.handle },
Command{ .name = "node", .action = "", .func = test_command.challengeNode },
Command{ .name = "set", .action = "", .func = test_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);
}

View file

@ -0,0 +1,14 @@
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, "You can enter MoC, PF, AS via menu\n", allocator);
try commandhandler.sendMessage(session, "(If your Castorice technique enabled, you must enter battle by using Castorice's technique)\n", allocator);
}

View 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);
}

View file

@ -0,0 +1,273 @@
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 API = @import("../api.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 AllAvatars = try API.AvatarList(allocator, null);
const config = try Config.loadGameConfig(allocator, "config.json");
var char = protocol.AvatarSync.init(allocator);
for (AllAvatars.items) |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);
}

View 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{1407};
pub var RateUpFourStars = [_]u32{ 1110, 1106, 1301 };
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;
}

View 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,
});
}

View 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,
});
}

235
gameserver/src/data.zig Normal file
View file

@ -0,0 +1,235 @@
// Avatar group
pub const MultiAvatar = [_]u32{
8002, 8004, 8006, 8008, 1001, 1224,
};
pub const McTracing = [_]u32{
8002, 8004, 8006, 8008,
};
pub const skills = [_]u32{ 1, 2, 3, 4, 7, 101, 102, 103, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 301, 302 };
pub const skills_old = [_]u32{ 1, 2, 3, 4, 7, 101, 102, 103, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210 };
// Battle group
//TODO: update every patch
pub const buffs_unlocked = &[_]u32{
100101, 100201, 100301, 100401, 100501, 100601, 100801, 100901, 101301,
110101, 110201, 110202, 110203, 110301, 110401, 110501, 110601, 110701,
110801, 110901, 111001, 111101, 111201, 120101, 120301, 120401, 120501,
120601, 120701, 120702, 120801, 120802, 120901, 121001, 121101, 121201,
121202, 121203, 121301, 121302, 121303, 121401, 121501, 121701, 121801,
122001, 122002, 122003, 122004, 122101, 122201, 122301, 122302, 122303,
122304, 122402, 122403, 122501, 130101, 130201, 130301, 130302, 130303,
130401, 130402, 130403, 130404, 130405, 130406, 130501, 130601, 130602,
130701, 130801, 130802, 130803, 130901, 130902, 130903, 131001, 131002,
131201, 131301, 131401, 131501, 131502, 131503, 131701, 131702, 140101,
140102, 140201, 140202, 140301, 140401, 140501, 140701, 800301, 800501,
800701,
};
//TODO: update more Remembrance character in future
pub const Rem = [_]u32{ 8007, 8008, 1402, 1407 };
//TODO: update id for characters have ignore toughness in their technique in future
pub const IgnoreToughness = [_]u32{ 1006, 1308, 1317 };
// Profile group
pub const OwnedChatBubbles = [_]u32{ 220000, 220001, 220002, 220003, 220004, 220005, 220006, 220007, 220008 };
pub const OwnedPhoneThemes = [_]u32{ 221000, 221001, 221002, 221003, 221004, 221005, 221006, 221007, 221008, 221010 };
pub const OwnedHeadIcon = [_]u32{
200101, 200102, 200103, 200104, 200105, 200106, 200107, 200108, 200109,
200110, 200111, 200112, 200113, 200114, 200115, 200116, 200117, 200118,
200119, 200120, 200121, 200122, 200123, 200124, 200125, 200126, 200127,
200128, 200129, 200130, 200131, 200132, 200133, 200134, 200135, 200136,
201001, 201002, 201003, 201004, 201005, 201006, 201008, 201009, 201013,
201101, 201102, 201103, 201104, 201105, 201106, 201107, 201108, 201109,
201110, 201111, 201112, 201201, 201202, 201203, 201204, 201205, 201206,
201207, 201208, 201209, 201210, 201211, 201212, 201213, 201214, 201215,
201217, 201218, 201220, 201221, 201222, 201223, 201224, 201225, 201301,
201302, 201303, 201304, 201305, 201306, 201307, 201308, 201309, 201310,
201312, 201313, 201314, 201315, 201317, 201401, 201402, 201403, 201404,
201405, 201407, 202001, 202002, 202003, 202004, 202005, 202006, 202007,
202008, 202009, 202010, 202011, 202012, 202013, 202014, 202015, 202016,
202017, 202018, 202019, 202020, 202025, 202028, 208001, 208002, 208003,
208004, 208005, 208006, 208007, 208008,
};
// Mission group
pub const FinishedMainMissionIdList = [_]u32{
1000101, 1000111, 1000112, 1000113, 1000114, 1000201, 1000202, 1000203, 1000204, 1000300, 1000301, 1000302, 1000303, 1000304, 1000400, 1000401, 1000402, 1000410, 1000500, 1000501, 1000502, 1000503, 1000504, 1000505, 1000510, 1000511, 1010001, 1010002, 1010101, 1010201, 1010202, 1010203, 1010204, 1010205, 1010206, 1010301, 1010302, 1010303, 1010401, 1010402,
1010403, 1010405, 1010500, 1010501, 1010502, 1010503, 1010601, 1010602, 1010700, 1010701, 1010801, 1010802, 1010901, 1010902, 1011001, 1011002, 1011003, 1011100, 1011101, 1011102, 1011103, 1011201, 1011202, 1011301, 1011400, 1011401, 1011402, 1011403, 1011501, 1011502, 1011503, 1020101, 1020201, 1020301, 1020302, 1020400, 1020401, 1020402, 1020403, 1020501,
1020601, 1020701, 1020702, 1020801, 1020901, 1021001, 1021101, 1021201, 1021301, 1021401, 1021501, 1021601, 1021702, 1021703, 1030101, 1030102, 1030201, 1030202, 1030301, 1030302, 1030303, 1030304, 1030401, 1030402, 1030403, 1030501, 1030601, 1030701, 1030702, 1030703, 1030801, 1030901, 1031001, 1031002, 1031101, 1031201, 1031301, 1031401, 1031501, 1031601,
1031701, 1031801, 1031901, 1032001, 1032101, 1032201, 1032301, 1032302, 1032401, 1032501, 1032601, 1032602, 1032701, 1032702, 1032703, 1032704, 1032705, 1032801, 1032802, 1032901, 1032902, 1033001, 1033101, 1033102, 1034101, 1034103, 1034104, 1034105, 1034107, 1034108, 1034109, 1034110, 1034201, 1034202, 1034203, 1034204, 1034205, 1034206, 1034207, 1034208,
1034209, 1034210, 1034211, 1034212, 1036001, 1036002, 1036003, 1036004, 1036102, 1036103, 1036104, 1036105, 1036106, 1040101, 1040102, 1040103, 1040104, 1040111, 1040105, 1040106, 1040107, 1040108, 1040109, 1040110, 1040112, 1040113, 1040114, 1040115, 1040116, 1040117, 2000001, 2000002, 2000003, 2000004, 2000100, 2000101, 2000102, 2000103, 2000104, 2000105,
2000106, 2000107, 2000108, 2000109, 2000110, 2000111, 2000112, 2000113, 2000116, 2000118, 2000119, 2000120, 2000122, 2000131, 2000132, 2000133, 2000201, 2000202, 2000203, 2000204, 2000205, 2000206, 2000207, 2000208, 2000209, 2000211, 2000212, 2000301, 2000302, 2000303, 2000304, 2000305, 2000310, 2000311, 2000312, 2000313, 2000314, 2000320, 2000701, 2000702,
2000703, 2000704, 2000705, 2000706, 2000707, 2000801, 2000802, 2000803, 2000901, 2000902, 2000903, 2001001, 2001002, 2001003, 2001101, 2001201, 2001301, 2001401, 2001501, 2010005, 2010201, 2010202, 2010203, 2010204, 2010205, 2010206, 2010301, 2010302, 2010401, 2010402, 2010405, 2010500, 2010501, 2010502, 2010503, 2010701, 2010702, 2010703, 2010705, 2010706,
2010708, 2010709, 2010720, 2010730, 2010731, 2010732, 2010733, 2010734, 2010735, 2010901, 2010902, 2010903, 2010904, 2010905, 2010906, 2010907, 2010908, 2010909, 2010910, 2010911, 2010912, 2011101, 2011102, 2011103, 2011104, 2011105, 2011301, 2011302, 2011303, 2011400, 2011401, 2011402, 2011403, 2011404, 2011405, 2011406, 2011407, 2011408, 2011409, 2011410,
2011411, 2011412, 2011413, 2011501, 2011502, 2011601, 2011701, 2011801, 2011901, 2011902, 2011903, 2011904, 2011905, 2011906, 2020103, 2020104, 2020105, 2020106, 2020107, 2020108, 2020109, 2020110, 2020111, 2020201, 2020202, 2020203, 2020204, 2020205, 2020301, 2020302, 2020303, 2020304, 2020305, 2020306, 2020307, 2020308, 2020309, 2020313, 2020314, 2020315,
2020316, 2020317, 2020318, 2020319, 2020401, 2020402, 2020403, 2020404, 2020405, 2020406, 2020407, 2020501, 2020502, 2020503, 2020504, 2020505, 2020506, 2020507, 2020601, 2020602, 2020603, 2020604, 2020701, 2020702, 2020703, 2020801, 2020802, 2020901, 2021001, 2021002, 2021009, 2021601, 2021602, 2021701, 2021702, 2021703, 2021704, 2021705, 2021801, 2021802,
2021803, 2021901, 2021902, 2022001, 2022002, 2022003, 2022004, 2022005, 2022006, 2022007, 2022008, 2022101, 2022102, 2022103, 2022104, 2022105, 2022201, 2022202, 2022203, 2022204, 2022205, 2022206, 2022301, 2022302, 2022303, 2022304, 2022305, 2022306, 2022307, 2022308, 2022309, 2030001, 2030002, 2030003, 2030101, 2030102, 2030201, 2030202, 2030203, 2030301,
2030302, 2030401, 2030402, 2030501, 2030601, 2030602, 2030603, 2031001, 2040101, 2040201, 2040202, 2040203, 2040301, 3000201, 3000202, 3000203, 3000211, 3000212, 3000213, 3000301, 3000302, 3000303, 3000522, 3000523, 3000524, 3000525, 3000526, 3000527, 3000601, 3000602, 3000603, 3000604, 3000701, 3000702, 3000703, 3000704, 3000705, 3000800, 3000801, 3000802,
3000803, 3010102, 3010103, 3010104, 3010105, 3010201, 3010202, 3010203, 3010204, 3010205, 3011011, 3011012, 3011013, 3011014, 3011111, 3011112, 3011113, 3011114, 3011201, 3011202, 3011203, 3011204, 3011205, 3011206, 3011207, 3011208, 3011401, 3011402, 3011403, 3011404, 3011405, 3011406, 3011407, 3011408, 3011501, 3011502, 3011503, 3011504, 3011505, 3011601,
3011602, 3011603, 3011604, 3011605, 3011606, 3011607, 3011608, 3011609, 3011610, 3012001, 3020101, 3020102, 3020103, 3020104, 3020105, 3020106, 3020107, 3020108, 3020201, 3020202, 3020203, 3020204, 3020205, 3020206, 3040010, 3040020, 3040030, 3040040, 3040050, 3040060, 4010105, 4010106, 4010107, 4010112, 4010113, 4010115, 4010116, 4010121, 4010122, 4010123,
4010124, 4010125, 4010126, 4010127, 4010128, 4010130, 4010131, 4010133, 4010134, 4010135, 4010136, 4010137, 4010138, 4010140, 4010141, 4015101, 4015102, 4015103, 4015202, 4015203, 4015204, 4015301, 4015302, 4015303, 4015401, 4015402, 4015403, 4015404, 4015502, 4015503, 4015504, 4015505, 4015601, 4015602, 4015700, 4015702, 4015703, 4015704, 4015705, 4015706,
4015707, 4015708, 4015709, 4015901, 4015801, 4015802, 4020101, 4020102, 4020103, 4020104, 4020105, 4020106, 4020107, 4020108, 4020109, 4020110, 4020111, 4020112, 4020113, 4020114, 4030001, 4030002, 4030003, 4030004, 4030006, 4030007, 4030009, 4030010, 4040001, 4040002, 4040003, 4040004, 4040005, 4040006, 4040007, 4040008, 4040009, 4040010, 4040011, 4040012,
4040014, 4040015, 4040017, 4040018, 4040019, 4040020, 4040021, 4040022, 4040023, 4040024, 4040051, 4040052, 4040053, 4040100, 4040101, 4040102, 4040103, 4040104, 4040105, 4040106, 4040107, 4040108, 4040109, 4040110, 4040114, 4040115, 4040116, 4040117, 4040118, 4040119, 4040120, 4040121, 4040122, 4040123, 4040124, 4040125, 4040126, 4040127, 4040128, 4040129,
4040130, 4040151, 4040152, 4040153, 4040154, 4040155, 4040156, 4040157, 4040158, 4040159, 4040160, 4040161, 4040162, 4040163, 4040164, 4040165, 4040166, 4040167, 4040168, 4040169, 4040170, 4040171, 4040172, 4040173, 4040174, 4040175, 4040176, 4040177, 4040178, 4040179, 4040180, 4040181, 4040182, 4040183, 4040184, 4040185, 4040186, 4040187, 4040188, 4040189,
4040190, 4040201, 4040202, 4040203, 4040204, 4040205, 4040206, 4040207, 4040208, 4040209, 4040210, 4040211, 4040212, 4040213, 4040214, 4040215, 4040216, 4040217, 4040218, 4040219, 4040220, 4040221, 4040222, 4040223, 4040224, 4040225, 4040226, 4040227, 4040228, 4040229, 4040230, 4040231, 4040240, 4040241, 4040242, 4040244, 4040245, 4040246, 4040247, 4040248,
4040249, 4040250, 4040251, 4040252, 4040253, 4040254, 4040255, 4040256, 4040257, 4040258, 4040259, 4040260, 4040261, 4040262, 4040263, 4040264, 4040265, 4040290, 4040300, 4040302, 4040303, 4040304, 4040305, 4040306, 4040307, 4040308, 4040309, 4040310, 4040311, 4040312, 4040313, 4040314, 4040315, 4040316, 4040317, 4040318, 4040319, 4040320, 4040321, 4040322,
4040323, 4040324, 4040325, 4040326, 4040327, 4040328, 4040329, 4040330, 4040331, 4040332, 4040333, 4040334, 4040335, 4040336, 4040337, 4040338, 4040339, 4040340, 4040341, 4040342, 4040343, 4040344, 4040345, 4040346, 4040347, 4040348, 4040349, 4040350, 4040351, 4040352, 4040353, 4040356, 4049901, 4049902, 4050005, 4050007, 4050008, 4050009, 4050010, 4050011,
4050012, 4050023, 4050028, 4050029, 4050030, 4050031, 4050032, 4050033, 4050034, 4050035, 4050036, 4050037, 4050038, 4050039, 4070011, 4070012, 4070013, 4071311, 4071312, 4071313, 4071320, 4071321, 4071322, 4072011, 4072012, 4072013, 4072021, 4072022, 4072023, 4072024, 4072121, 4072122, 4072123, 4081311, 4081312, 4081313, 4081314, 4081315, 4081316, 4081317,
4081318, 4122101, 4122102, 4122103, 4140101, 4140102, 4140103, 4140104, 4140105, 4140106, 4140107, 4140108, 4140109, 4140110, 4140111, 4140112, 4140113, 4140114, 4140115, 4140116, 4140117, 4140118, 4140119, 4140120, 4140121, 4140122, 4140123, 4240301, 4240302, 4240304, 4240305, 4240306, 4240307, 4240308, 4240309, 4240310, 4240311, 4240312, 4240313, 4240314,
4240316, 4240317, 4240320, 4240321, 4240322, 4240323, 4240324, 4240401, 4240402, 4240403, 4240404, 4240405, 4240406, 4240407, 4340101, 4340102, 4340103, 4340104, 4340105, 4340106, 4340107, 4340108, 4340109, 4340110, 4340111, 4340112, 4340113, 4340114, 4340115, 4340116, 4340117, 4340118, 4340119, 4340120, 4340121, 4340122, 4340123, 4340124, 4340125, 4340126,
4340127, 4340128, 4340129, 4340130, 4340131, 4340132, 4340133, 4340134, 4340135, 4340136, 4340137, 4340138, 4340139, 4340140, 4340141, 4340142, 4340144, 4340145, 4340146, 4340147, 4340148, 4340149, 4340150, 4340151, 4340152, 4340153, 4340154, 4340155, 4340156, 4340157, 4340158, 4340159, 4340160, 4340161, 4340162, 4340163, 4340164, 4340165, 4340166, 4340167,
4340168, 4340169, 4340170, 4340171, 4540101, 4540102, 4540103, 4540104, 4540105, 4540106, 4540107, 4540111, 4540112, 4540113, 4540114, 4540201, 4540202, 4540203, 4540204, 4540205, 4540206, 4540211, 4540212, 4540213, 4540214, 4540301, 4540302, 4540303, 4540304, 4540305, 4540306, 4540307, 4540311, 4540312, 4540313, 4540314, 4540315, 4540401, 4540402, 4540403,
4540404, 4540405, 4540406, 4540407, 4540408, 4540411, 4540412, 4540413, 4540414, 4540501, 4540502, 4540503, 5000401, 5000402, 5000403, 5000404, 5000405, 5000406, 5000407, 5000408, 5000409, 5030500, 6020101, 6020201, 6020202, 8000001, 8000002, 8000101, 8000102, 8000104, 8000105, 8000131, 8000132, 8000133, 8000134, 8000135, 8000136, 8000137, 8000138, 8000139,
8000151, 8000152, 8000153, 8000154, 8000155, 8000156, 8000157, 8000158, 8000159, 8000161, 8000162, 8000170, 8000171, 8000172, 8000173, 8000174, 8000175, 8000177, 8000178, 8000180, 8000181, 8000182, 8000183, 8000184, 8000185, 8000186, 8000187, 8000188, 8000189, 8000201, 8000202, 8000203, 8000204, 8001201, 8001202, 8001203, 8001204, 8001205, 8001206, 8001207,
8001208, 8001209, 8001210, 8001211, 8001212, 8001213, 8001214, 8001215, 8001216, 8001217, 8001218, 8001219, 8001220, 8001221, 8001222, 8001223, 8001224, 8001225, 8001226, 8001227, 8001241, 8001242, 8001243, 8001244, 8001251, 8001252, 8001253, 8001254, 8001255, 8001261, 8001262, 8001263, 8001264, 8001265, 8001266, 8001267, 8001268, 8002100, 8002101, 8002102,
8002103, 8002104, 8002105, 8002106, 8002107, 8002201, 8002202, 8002211, 8002212, 8002213, 8002214, 8002221, 8002222, 8002231, 8002232, 8002233, 8002234, 8003101, 8003102, 8003201, 8003202, 8003203, 8003204, 8003205, 8003206, 8003207, 8003208, 8003209, 8003210, 8003211, 8003212, 8003213, 8003214, 8003215, 8003216, 8003217, 8003218, 8003219, 8003220, 8003221,
8003222, 8003240, 8003241, 8003242, 8003243, 8003244, 8003245, 8003246, 8003247, 8003248, 8003249, 8003250, 8003251, 8003260, 8003261, 8003262, 8003263, 8003264, 8003265, 8003266, 8003267, 8003268, 8003269, 8003278, 8003279, 8003280, 8003281, 8003282, 8003283, 8003284, 8011401, 8012101, 8012102, 8012103, 8012104, 8012105, 8012106, 8012107, 8012401, 8013101,
8013102, 8013103, 8013104, 8013105, 8013106, 8013107, 8013108, 8013109, 8013110, 8014101, 8014102, 8014103, 8014104, 8014105, 8014106, 8014112, 8014121, 8014122, 8014123, 8014124, 8014125, 8014126, 8014127, 8014128, 8014129, 8014130, 8014131, 8014132, 8014133, 8014134, 8014135, 8014136, 8014137, 8014138, 8014139, 8014140, 8014141, 8014142, 8014143, 8014144,
8014145, 8014146, 8014147, 8014148, 8014149, 8014150, 8014161, 8014162, 8014163, 8014164, 8014165, 8014166, 8014167, 8014168, 8015101, 8015102, 8015103, 8015104, 8015111, 8015112, 8015113, 8015114, 8015123, 8015150, 8015151, 8015152, 8015153, 8015154, 8015155, 8015156, 8015157, 8015158, 8015159, 8015161, 8015162, 8015163, 8015164, 8015165, 8015171, 8015172,
8015173, 8015180, 8015191, 8015192, 8015193, 8015194, 8015195, 8015196, 8015197, 8015198, 8015201, 8015202, 8015203, 8016101, 8016102, 8016103, 8016104, 8016105, 8016106, 8016201, 8016202, 8016203, 8016204, 8016205, 8016206, 8016207, 8016208, 8016209, 8016210, 8016211, 8016212, 8016213, 8016301, 8016302, 8016303, 8016304, 8016305, 8017101, 8017102, 8020101,
8020102, 8020103, 8020104, 8020105, 8020106, 8020107, 8020108, 8020201, 8020202, 8020203, 8020204, 8020205, 8020206, 8020207, 8020208, 8020209, 8020210, 8020211, 8020212, 8020213, 8020214, 8020215, 8020216, 8020217, 8020218, 8020219, 8020220, 8020221, 8020222, 8020223, 8020224, 8020231, 8020232, 8020233, 8020234, 8020236, 8020300, 8020301, 8020302, 8020303,
8020304, 8020305, 8021100, 8021101, 8021102, 8021103, 8021104, 8021105, 8021106, 8021121, 8021122, 8021123, 8021124, 8021130, 8021131, 8021132, 8021201, 8021202, 8021203, 8021301, 8021302, 8021303, 8021401, 8021402, 8021403, 8021404, 8021501, 8021502, 8021503, 8021504, 8021505, 8021506, 8022101, 8022102, 8022103, 8022104, 8022105, 8022106, 8022107, 8022108,
8022109, 8022110, 8022111, 8022112, 8022113, 8022114, 8022115, 8022120, 8022121, 8022122, 8022123, 8022124, 8022125, 8022126, 8022127, 8022201, 8022202, 8022301, 8022302, 8022401, 8022402, 8023101, 8023102, 8023103, 8023104, 8023105, 8023111, 8023201, 8023202, 8023203, 8023301, 8023401, 8023501, 8023601, 8023701, 8023801, 8024101, 8024111, 8024112, 8024113,
8024114, 8024115, 8024116, 8024201, 8024202, 8024203, 8024204, 8024205, 8024206, 8024207, 8024208, 8024301, 8024302, 8024303, 8024304, 8024305, 8024306, 8024307, 8025101, 8025102, 8025103, 8025104, 8025105, 8025106, 8025107, 8025111, 8025112, 8025113, 8025114, 8025131, 8025132, 8025133, 8025134, 8025135, 8025136, 8025137, 8025138, 8025139, 8025140, 8025141,
8025142, 8025201, 8025202, 8025210, 8025211, 8025212, 8025213, 8025214, 8025215, 8025216, 8025217, 8025218, 8025219, 8025220, 8025221, 8025222, 8025223, 8025224, 8025225, 8025226, 8025227, 8025228, 8025229, 8025230, 8025231, 8025232, 8025233, 8025301, 8025302, 8026101, 8026102, 8026103, 8026104, 8026105, 8026110, 8026111, 8026112, 8026113, 8026114, 8026115,
8026116, 8026117, 8026118, 8026120, 8026121, 8026201, 8026202, 8026203, 8026204, 8026205, 8026206, 8026207, 8026208, 8026209, 8026301, 8026302, 8026401, 8026402, 8027111, 8027112, 8027113, 8027114, 8027115, 8027116, 8027117, 8027118, 8027119, 8027120, 8027121, 8027200, 8027201, 8027202, 8027203, 8027204, 8027205, 8027206, 8027207, 8027208, 8027209, 8027301,
8030101, 8030102, 8030103, 8030104, 8030105, 8030106, 8030107, 8030111, 8030112, 8030113, 8030114, 8030115, 8030121, 8030122, 8030123, 8030124, 8030125, 8030131, 8030132, 8030133, 8030141, 8030142, 8030143, 8030300, 8030301, 8030302, 8030303, 8030304, 8030305, 4050013, 4050014, 4050015, 4050016, 4050017, 4050018, 4050019, 4050020, 4050021, 4050022, 4050024,
4050025, 4050026, 4050027,
};
pub const FinishedSubMissionIdList = [_]u32{
100010103, 100010121, 100020118, 100030204, 100050102, 101000205, 101010114, 101020209, 101120201, 102010112, 102020110,
102050110, 102060111, 102120101, 102170204, 103010114, 103020111, 103040110, 103050101, 103060129, 103150120, 103240101,
103270105, 200100129, 201020325, 202170101, 202210428, 802410103, 801710102,
};
pub const FinishedTutorialIdList = [_]u32{
1001, 1002, 1003, 1004, 1005, 1007, 1008, 1010, 2000, 2001, 2002, 2003, 2004, 2008, 1001, 1002, 1003, 1004, 1005, 1007, 1008, 1010, 2000, 2001, 2002, 2003, 2004, 2008, 2010, 2011, 2010, 2011, 2012, 2013, 3001, 3002, 3003, 3004, 3005, 3006, 3008, 3009, 3010, 3011, 2012, 2013,
3001, 3002, 3003, 3004, 3005, 3006, 3008, 3009, 3010, 3011, 3012, 3202, 4002, 4003, 3012, 3202, 4002, 4003, 4004, 4005, 4006, 4007, 4008, 4009, 4010, 4011, 4012, 4013, 4004, 4005, 4006, 4007, 4008, 4009, 4010, 4011, 4012, 4013, 4014, 4015, 4016, 4017, 4018, 4019, 4014, 4015,
4016, 4017, 4018, 4019, 4020, 4021, 4022, 4023, 4024, 4025, 4026, 4027, 4020, 4021, 4022, 4023, 4024, 4025, 4026, 4027, 4028, 4029, 4030, 4031, 4032, 4033, 4034, 4035, 4028, 4029, 4030, 4031, 4032, 4033, 4034, 4036, 4037, 4038, 4039, 4040, 5001, 5002, 4036, 4037, 4038, 4039,
4040, 4041, 4042, 4043, 4044, 4045, 4046, 4047, 4048, 4049, 4050, 4051, 4052, 4053, 4054, 4055, 4056, 4057, 4058, 4059, 4060, 4061, 4062, 4063, 5001, 5002, 5003, 5004, 5003, 5004, 5005, 5006, 5007, 5008, 5009, 5010, 5011, 5012, 5013, 5014, 5015, 5016, 5005, 5006, 5007, 5008,
5009, 5010, 5011, 5012, 5013, 5014, 5015, 5016, 5017, 5018, 5019, 5021, 5017, 5018, 5019, 5021, 5022, 5023, 5024, 5025, 5026, 5027, 5028, 5029, 5030, 5031, 5022, 5023, 5024, 5025, 5026, 5027, 5028, 5029, 5030, 5031, 5032, 5033, 5034, 5035, 5036, 5038, 5032, 5033, 5034, 5035,
5036, 5038, 5039, 5041, 5044, 5045, 5046, 5047, 5048, 5049, 5039, 5041, 5044, 5045, 5046, 5047, 5048, 5049, 5050, 5051, 5052, 5053, 5054, 5055, 5056, 5057, 5050, 5051, 5052, 5053, 5054, 5055, 5056, 5057, 5058, 5059, 5060, 5061, 5062, 5063, 5058, 5059, 5060, 5061, 5062, 5063,
5064, 5065, 5070, 5071, 5072, 5073, 5074, 5075, 5077, 5078, 5064, 5065, 5070, 5071, 5072, 5073, 5074, 5075, 5077, 5078, 5079, 5081, 5082, 5083, 5079, 5081, 5082, 5083, 5084, 5085, 5086, 5087, 5088, 5089, 5090, 5091, 5092, 5093, 5094, 5095, 5084, 5085, 5086, 5087, 5088, 5089,
5090, 5091, 5092, 5093, 5094, 5095, 5096, 5097, 5096, 5097, 5098, 5099, 5100, 5101, 5102, 5103, 5104, 5105, 5106, 5107, 5108, 5109, 5110, 5111, 5098, 5099, 5100, 5101, 5102, 5103, 5104, 5105, 5106, 5107, 5108, 5109, 5110, 5111, 5112, 5113, 5114, 5115, 5116, 5130, 5131, 5132,
5133, 5134, 5135, 5140, 5141, 5142, 5112, 5113, 5114, 5115, 5116, 5130, 5131, 5132, 5133, 5134, 5135, 5140, 5141, 5142, 5143, 5144, 5143, 5144, 5145, 5146, 5150, 5151, 5152, 5153, 5154, 5155, 5156, 5157, 5158, 5159, 5145, 5146, 5150, 5151, 5152, 5153, 5154, 5155, 5156, 5157,
5158, 5159, 5160, 5161, 5162, 5163, 5160, 5161, 5162, 5172, 5173, 5174, 5175, 5176, 5177, 5178, 5179, 5180, 5181, 5182, 5164, 5172, 5173, 5174, 5175, 5176, 5177, 5178, 5179, 5180, 5181, 5182, 5183, 5184, 5185, 5186, 5183, 5184, 5185, 5186, 5187, 5188, 5189, 5190, 5191, 5192,
5193, 5194, 5195, 5196, 5187, 5188, 5189, 5190, 5191, 5192, 5193, 5194, 5195, 5196, 5197, 5198, 5199, 5200, 5201, 5202, 5197, 5198, 5199, 5200, 5201, 5202, 5301, 5302, 5303, 5304, 5305, 5306, 5308, 5309, 5210, 5211, 5212, 5213, 5214, 5215, 5216, 5217, 5218, 5219, 5220, 5221,
5222, 5301, 5302, 5303, 5304, 5305, 5306, 5308, 5309, 5310, 5322, 5326, 5312, 5311, 5313, 5307, 5314, 5316, 5317, 5318, 5310, 5322, 5326, 5312, 5311, 5313, 5307, 5314, 5315, 5316, 5317, 5318, 5319, 5320, 5319, 5320, 5321, 5323, 5324, 5325, 5350, 5387, 5352, 5388, 5355, 5360,
5361, 5356, 5357, 5358, 5321, 5323, 5324, 5325, 5350, 5387, 5352, 5388, 5355, 5360, 5361, 5356, 5357, 5358, 5359, 5351, 5365, 5366, 5367, 5368, 5375, 5376, 5377, 5389, 5374, 5369, 5370, 5371, 5359, 5351, 5365, 5366, 5367, 5368, 5375, 5376, 5377, 5389, 5374, 5369, 5370, 5371,
5372, 5373, 5372, 5373, 5390, 5380, 5381, 5400, 5382, 5383, 5392, 5378, 5384, 5393, 5408, 5394, 5390, 5380, 5381, 5400, 5382, 5383, 5392, 5378, 5384, 5393, 5408, 5394, 5395, 5391, 5385, 5397, 5395, 5391, 5385, 5397, 5398, 5399, 5386, 5401, 5402, 5403, 5404, 5405, 5406, 5407,
5398, 5399, 5386, 5401, 5402, 5403, 5404, 5405, 5406, 5407, 5409, 5410, 5450, 5451, 5452, 5453, 5409, 5410, 5510, 5511, 7001, 7002, 7003, 7004, 7005, 7007, 7008, 7009, 9001, 9002, 5454, 5455, 5456, 5457, 5458, 5459, 5460, 5461, 5462, 5463, 5464, 5465, 5510, 5511, 5512, 5513,
5514, 5520, 5521, 5522, 5523, 5524, 5525, 5530, 5531, 5532, 5533, 5534, 5535, 5536, 5537, 5540, 5555, 5556, 5560, 5561, 5562, 5563, 5564, 5565, 8001, 8002, 8003, 8005, 8006, 8007, 8009, 8012, 5600, 5601, 5602, 5603, 5604, 5605, 5606, 5607, 5608, 5609, 5610, 5611, 5612, 5613,
5614, 5615, 5700, 5701, 5702, 5703, 5704, 5705, 5706, 5707, 5708, 5709, 5710, 5711, 5712, 5713, 5714, 5717, 5718, 5801, 5802, 5803, 5804, 5805, 5806, 5810, 5811, 5812, 5813, 5814, 5815, 5715, 5716, 7001, 7002, 7003, 7004, 7005, 7006, 7007, 7008, 7009, 7010, 7011, 7012, 7013,
7014, 7015, 9001, 9002, 8013, 8014, 8015, 8016, 8017, 8018, 8019, 8020, 8021, 5566, 5567, 5568, 9003, 9004, 9005, 9006, 9007, 9008, 9009, 9010, 9011, 9012, 9013, 9014, 9015, 9016, 9003, 9004, 9005, 9006, 9007, 9008, 9009, 9010, 9011, 9012, 9013, 9014, 9015, 9016, 9017, 9018,
9017, 9018, 9019, 9020, 9021, 9022, 9023, 9024, 9025, 9026, 9027, 9028, 9029, 9030, 9019, 9020, 9021, 9022, 9023, 9024, 9025, 9026, 9027, 9028, 9029, 9030, 9031, 9032, 9033, 9034, 9031, 9032, 9033, 9034, 9035, 9036, 9037, 9038, 9039, 9040, 9041, 9042, 9043, 9044, 9035, 9036,
9037, 9038, 9039, 9040, 9041, 9042, 9043, 9044, 9045, 9046, 9047, 9048, 9049, 9050, 9045, 9046, 9047, 9048, 9049, 9050, 9051, 9052, 9053, 9201, 9202, 9203, 9204, 9205, 9051, 9052, 9053, 9054, 9055, 9056, 9057, 9058, 9059, 9060, 9061, 9062, 9063, 9065, 9066, 9067, 9070, 9071,
9072, 9073, 9074, 9075, 9076, 9077, 9078, 9079, 9080, 9081, 9082, 9083, 9084, 9085, 9086, 9201, 9202, 9203, 9204, 9205, 9206, 9207, 9208, 9209, 9210, 9211, 9212, 9213, 9214, 9301, 9206, 9207, 9208, 9209, 9210, 9211, 9212, 9213, 9214, 9301, 9302, 9303, 9304, 9305, 9302, 9303,
9304, 9305, 9306, 9307, 9308, 9309, 9310, 9311, 9313, 9314, 9315, 9316, 9317, 9318, 9306, 9307, 9308, 9309, 9310, 9311, 9313, 9314, 9315, 9316, 9317, 9318, 9319, 9321, 9319, 9321, 9322, 9323, 9324, 9325, 9801, 9802, 9803, 9804, 9805, 9806, 9807, 9808, 9809, 9810, 9322, 9323,
9324, 9325, 9801, 9802, 9803, 9804, 9805, 9806, 9807, 9808, 9809, 9810, 9621, 9622, 9623, 9624, 9625, 9626, 9627, 9628, 9629, 9630, 9631, 9701, 9702, 9703, 9625, 9626, 9627, 9628, 9629, 9630, 9631, 9701, 9702, 9703, 9704, 5550, 5551, 5552, 5553, 5554, 9704, 1011, 2005, 2009,
2014, 2015, 5530, 5536, 9811, 9812, 9813, 9814, 9816, 9817, 9818, 9819, 9815, 9820, 9901, 9903, 9904, 9905, 9811, 9812, 9813, 9814, 9816, 9817, 9818, 9819, 9815, 9820, 9901, 9903, 9904, 9905, 9906, 9907, 9906, 9907, 9908, 9610, 9611, 9612, 9613, 9614, 9615, 9616, 9617, 9618,
9619, 9620, 9908, 9610, 9611, 9612, 9613, 9614, 9615, 9616, 9617, 9618, 9619, 9620, 9621, 9622, 9623, 9624,
};
pub const TutorialGuideIdList = [_]u32{
2229, 2220, 1117, 9321, 9203, 2211, 2231, 9306, 2106, 3215, 2210, 3105, 9330, 9331, 9107, 5102, 9204, 9202, 9201, 2008, 4105, 3212, 5104, 5103, 3109, 3207, 3107, 3216, 3108, 4106, 9205,
2230, 5106, 2202, 2224, 2217, 9117, 2218, 6062, 2219, 3213, 1101, 1103, 6002, 6063, 2225, 6027, 2223, 9119, 3106, 2200, 1105, 2228, 6018, 4102, 9304, 2221, 6050, 1104, 1108, 2208, 2226,
9303, 3203, 6129, 2209, 2212, 2214, 9114, 6040, 9120, 9113, 6073, 6037, 2206, 3007, 2204, 2227, 1109, 9115, 9301, 6003, 3201, 2213, 4103, 2233, 6128, 1118, 6120, 9116, 2201, 2203, 1102,
2205, 1110, 9305, 4101, 9118, 2216, 2232, 6004, 2007, 2207, 4001, 2107, 2222, 2215, 4110, 3205, 8073, 6130, 9334, 9333,
};
pub const FinishedTutorialGuideIdList = [_]u32{
221201, 221202, 221701, 221601, 221501, 221401, 111801, 111802, 221001, 221101, 500101, 500102, 210601, 221301, 220701, 220601, 220301, 220501, 220201, 220401, 220101, 222401, 970101, 970102, 221801, 210101, 110501, 220002, 222301, 220001, 222701, 110502, 110401, 110301, 110105, 110102, 110103, 110104, 110101, 110901,
110801, 600901, 600902, 600903, 601001, 601002, 601101, 601102, 601201, 601202, 601702, 602001, 602101, 602102, 602103, 602301, 602302, 602303, 602601, 601203, 603101, 603102, 603301, 603401, 603501, 603502, 603801, 603802, 603803, 603901, 604001, 604002, 604101, 604102, 604103, 604104, 604105, 604201, 604202, 604203,
604301, 604302, 604401, 604402, 604403, 604404, 604501, 604502, 604601, 604602, 604603, 605001, 605003, 605002, 605101, 605102, 605103, 605104, 605501, 605601, 605602, 605603, 605701, 605702, 605703, 605801, 605901, 606001, 606101, 606201, 606203, 606301, 606302, 606303, 606401, 606402, 606403, 606404, 606501, 606502,
606503, 606504, 606505, 606506, 606509, 606701, 606801, 606802, 606901, 607001, 607002, 606507, 606508, 606601, 606602, 606603, 606604, 606605, 607601, 607602, 608901, 609501, 609601, 609602, 609603, 609604, 609701, 609801, 609802, 610001, 610002, 605403, 605404, 610301, 610401, 610501, 610502, 610503, 610504, 610601,
610602, 610603, 610701, 610702, 610901, 611001, 611002, 611101, 611201, 611301, 611302, 611303, 611304, 611401, 611402, 611404, 611403, 611408, 611701, 611702, 611703, 611704, 611705, 611801, 611802, 611803, 611901, 611902, 611903, 612001, 611405, 611407, 612601, 612602, 612701, 612801, 612901, 612902, 630101, 630102,
630201, 630203, 630301, 630302, 630401, 630501, 630502, 630601, 630701, 630702, 630703, 630801, 630802, 630901, 630902, 630903, 631001, 631101, 631201, 631202, 631301, 631302, 631401, 631402, 631403, 631501, 631502, 631503, 631601, 631602, 631603, 631701, 631702, 631703, 631801, 631901, 630202, 632901, 632902, 633201,
633301, 633401, 633501, 633502, 633601, 633701, 633801, 633901, 633902, 633903, 634001, 634002, 634003, 630103, 634201, 634301, 709001, 709101, 709102, 709103, 709201, 806201, 806202, 806203, 602701, 602702, 602703, 605301, 601401, 601501, 601502, 601503, 601504, 605203, 605204, 600101, 600102, 600201, 600202, 600203,
600301, 600302, 600303, 605401, 605402, 801109, 801110, 600801, 600802, 605201, 605202, 612501, 612502, 612503, 510602, 510603, 510604, 510601, 600401, 600402, 510501, 510401, 510301, 510201, 510101, 300701, 200801, 200701, 100601, 200601, 410701, 410901, 410601, 310801, 310701, 310901, 400102, 400101, 210401, 310502,
320101, 320102, 320103, 320104, 320201, 320301, 320302, 320401, 320402, 320501, 320601, 320602, 320701, 320801, 320802, 320901, 320902, 321101, 321102, 321201, 321202, 321203, 321204, 321301, 321306, 321303, 321309, 321302, 321305, 321304, 321307, 321308, 321501, 321502, 321601, 321602, 410501, 410502, 411001, 310501,
321701, 210501, 210502, 221901, 110701, 222001, 222002, 222501, 222502, 222601, 222602, 830301, 222802, 222801, 222803, 222901, 223001, 223101, 223201, 223301, 932301, 932302, 932303, 932304, 932401, 932402, 932403, 932404, 932405, 931901, 931902, 931903, 931801, 931802, 931803, 931701, 931702, 931703, 931601, 931602,
931603, 931501, 931502, 931503, 930808, 930801, 930809, 932001, 932002, 932003, 931401, 931402, 931301, 931302, 931303, 931201, 931205, 931202, 931203, 931204, 931001, 931002, 930901, 930902, 930601, 930602, 930603, 930604, 930605, 930401, 930501, 930502, 930301, 930302, 930101, 911901, 912001, 911801, 911802, 911803,
911701, 911601, 911501, 911401, 911301, 911302, 911201, 911101, 911001, 910901, 910801, 910701, 910501, 910401, 910301, 910201, 910101, 321801, 321802, 111901, 111902, 322101, 322102, 410201, 410301, 410401, 602801, 602802, 602803, 603201, 603601, 603602, 603603, 603701, 603702, 603703, 604701, 604702, 604801, 604802,
604901, 604902, 710001, 710101, 750101, 750201, 750202, 750302, 750303, 750401, 750501, 750601, 750602, 750701, 750801, 750901, 751101, 751401, 751402, 751501, 800101, 800102, 800501, 800201, 800202, 800203, 800204, 800301, 800302, 800402, 800403, 800401, 800205, 800802, 800801, 801001, 801020, 801002, 801003, 801008,
801026, 801004, 801006, 801007, 801009, 801010, 801011, 801012, 801024, 801013, 801014, 801023, 801015, 801021, 801016, 801017, 801018, 801019, 801005, 801101, 801102, 801103, 801105, 801106, 801201, 801203, 801205, 801207, 801208, 801022, 801025, 805501, 805502, 805503, 805506, 805504, 805505, 805507, 801107, 801108,
801119, 806301, 806302, 806601, 806602, 806603, 801120, 801121, 806901, 806902, 807001, 807002, 807003, 807004, 807201, 807301, 807302, 807304, 807303, 807401, 807405, 807403, 807501, 807502, 807601, 807602, 807801, 807802, 807901, 807902, 808001, 809001, 809002, 809101, 809102, 809103, 809201, 809202, 809301, 809302,
809303, 809401, 809402, 809403, 809501, 809601, 809602, 810001, 810002, 810003, 810101, 810102, 810201, 810202, 810301, 810302, 810401, 810402, 810501, 810502, 810601, 810602, 810701, 810702, 810801, 810802, 810901, 810902, 811001, 811003, 811101, 811102, 811201, 811202, 811301, 811302, 812201, 812202, 812203, 812204,
813601, 813602, 813603, 813604, 813605, 813101, 813201, 813301, 813401, 813501, 813801, 813802, 813804, 813803, 814001, 814002, 814003, 814004, 814101, 814102, 814201, 814202, 814301, 814302, 814501, 814502, 814601, 814602, 814701, 814702, 814703, 814801, 814802, 814803, 814901, 814902, 815001, 815002, 815101, 815102,
815202, 815502, 815301, 815302, 815401, 815402, 815501, 815601, 815602, 815701, 815702, 815801, 815802, 815803, 816001, 816102, 816101, 816201, 816202, 816301, 816401, 816402, 816403, 816501, 816601, 816701, 816702, 816703, 817001, 817002, 817101, 817102, 817103, 817201, 817202, 817203, 817301, 817302, 817401, 817402,
817501, 817502, 817503, 817504, 817601, 817602, 818001, 818002, 818004, 818003, 818101, 818102, 818201, 818202, 818203, 818301, 818302, 818401, 818402, 818403, 818501, 818502, 818601, 818602, 818701, 818702, 818801, 818802, 818901, 819001, 819002, 819003, 819102, 819103, 819201, 819202, 819205, 819203, 819204, 819301,
819302, 819401, 819402, 819403, 819404, 819501, 819502, 819503, 819505, 819504, 819601, 819602, 819701, 819702, 819801, 819901, 820001, 820002, 820101, 820102, 820201, 820301, 820302, 820401, 820501, 820601, 830101, 830102, 830103, 830104, 830201, 830204, 830205, 830206, 830203, 830106, 831001, 920101, 920102, 920201,
920202, 920301, 920302, 920401, 920402, 920501, 920601, 920602, 920603, 920701, 920702, 920703, 920801, 920802, 920901, 920902, 921001, 921002, 921101, 921201, 921202, 932101, 932102, 932103, 932501, 932601, 932701, 932702, 933001, 933002, 933003, 933101, 960101, 960102, 960103, 960104, 960201, 960501, 970201, 980101,
980102, 980201, 980202, 980401, 981101, 981102, 981201, 981301, 981401, 981501, 981502, 981601, 981801, 981901, 990101, 990102, 990301, 990302, 990401, 990402, 990403, 990501, 990502, 992001, 992002, 992004, 992003, 992101, 992102, 992103, 992201, 992203, 992202, 992301, 992302, 992303, 992501, 992502, 992601, 992602,
3100101, 3110201, 3110301, 3110501, 3110601, 3110901, 3120401, 3120601,
};
pub const ItemList = [_]u32{ 251001, 251002, 101 };
// somehow 30002 return black screen
pub const EventList = [_]u32{
10012, 10014, 10015, 10016, 10017, 10018, 10019, 10020, 10021, 10023, 10024, 10025, 10026, 10027, 10028, 10029, 10030,
10031, 10032, 10033, 10034, 10036, 10042, 10035, 10037, 10038, 10039, 10040, 10041, 10043, 10044, 10045, 10054, 10046,
10055, 10047, 10048, 10049, 10050, 10051, 10052, 10053, 10056, 10057, 10058, 10059, 10060, 10061, 10062, 10063, 10064,
10065, 10066, 10067, 10068, 10069, 10070, 10071, 10072, 10073, 10074, 10075, 10077, 10078, 10079, 10080, 10081, 10082,
10083, 10084, 10086, 10087, 10088, 10089, 10090, 10091, 10092, 10101, 20001, 20002, 20004, 20005, 20006, 20007, 20008,
20009, 20010, 20011, 20012, 20013, 20014, 20016, 20017, 20018, 20019, 20020, 20021, 20022, 20023, 20024, 20027, 20030,
20033, 20036, 20039, 20042, 20046, 20049, 20052, 20053, 20056, 30003, 40001, 40002, 40003, 40004, 40005, 50000, 50001,
50002, 30004, 30005, 30006, 30007, 60001, 60003, 60004, 60005, 60006, 60007, 60008, 60009, 60010, 60011, 60012, 60013,
60014, 60015, 60016, 60017, 60018, 70001, 70002, 70003, 70004, 70005, 70006, 99901, 50003, 22001, 22002, 22003, 22004,
22005, 22006, 22007, 22008, 50004, 80001, 80002, 80003, 80004, 80005, 80006, 80007, 80008, 80009, 80010, 80011, 80012,
80015, 50005, 50006, 30008, 30009, 50007, 30010, 50008, 50009, 50010, 50011, 50012, 50013, 50014, 50015, 50016, 50017,
50018, 50019, 50020, 50021, 50022, 50023, 50024, 50025, 30011, 30012, 30013, 50026, 40006, 50027, 50028, 30014, 50029,
50030, 50031, 50033, 99998, 99999, 50034, 50040,
};
pub const TeleportList = [_]u32{
1000002, 1000003, 1000101, 1000102, 1000201, 1010101, 1010102, 1010103, 1010104, 1010105, 1010106, 1010107, 1010109, 1010110,
1010111, 1010112, 1010201, 1010202, 1010203, 1010204, 1010205, 1020101, 1020102, 1020201, 1020202, 1020203, 1020204, 1020205,
1030101, 1030102, 1030103, 1030104, 1030105, 1030106, 1030107, 1030108, 1030109, 1030401, 1030402, 1030403, 1030501, 1030502,
1030503, 1030601, 1030602, 1030603, 1030604, 1030611, 1030612, 1030613, 1030614, 1030615, 1030616, 1030617, 1030618, 1030619,
1034101, 1034102, 1034103, 1034104, 1040101, 1040102, 1040103, 1040104, 1040105, 1040106, 1040107, 1040108, 1040151, 1040152,
1040153, 1040154, 1040155, 1040156, 1040161, 1040162, 1040201, 1040301, 2000101, 2000201, 2000301, 2000302, 2000401, 2010101,
2010102, 2011101, 2012101, 2012201, 2012202, 2012301, 2013101, 2013201, 2013301, 2013302, 2013401, 2013402, 2013501, 2013601,
2013603, 2021101, 2021201, 2021301, 2021302, 2021401, 2022101, 2022201, 2022301, 2023101, 2023102, 2023201, 2023202, 2023301,
2024101, 2024201, 2024301, 2031101, 2031102, 2031103, 2031104, 2031201, 2031202, 2031301, 2031302, 2031303, 2032101, 2032201,
2032301, 2033101, 2033201, 2041101, 2041102, 2041201, 2041202, 2041301, 2042101, 2042201, 2042301, 2042401, 3000101, 3000201,
3000202, 3000203, 3000204, 3000205, 3000206, 3000301, 3000401, 3000501, 3002001, 3012101, 3012102, 3012201, 3012202, 3012301,
3012302, 3012401, 3012402, 3012501, 3012601, 3012602, 3012603, 3012701, 3012702, 3013001, 3013002, 3013101, 3013102, 3013201,
3020101, 3020201, 3020301, 3020401, 3020501, 3020601, 4000001, 4000201, 4000202, 4000203, 4000204, 4000211, 4000212, 4000213,
4000214, 4000301, 4010101, 4010102, 4010103, 4010104, 4010106, 4011101, 4012201, 4012301, 4222101, 8000001, 8000101, 8000102,
8000103, 8011201, 8020001, 8020002, 8020003, 8020004, 8020005, 8020006, 8020007, 8020008, 8020101, 8020201, 8020301, 8020401,
8020501, 8020601, 8020701, 8020801, 8020901, 8021001, 8021101, 8030101, 8030102, 8030201, 8030202, 8030301, 8030401, 8030501,
8040001, 8040101, 8040201, 8040301, 8040401, 8040501, 8040601, 8040701, 8040801, 8040802, 8040803, 8040804, 8040805, 8040806,
8040807, 8040808, 8040901, 8040902, 8040903, 8040904, 8040905, 8040906, 8040907, 8050101, 8050201, 8050301, 8050401, 8050501,
8050601, 8050701, 8050801, 8050802, 8050803, 8050804, 8050805, 8050806, 8050807, 8060101, 8060201, 8060301, 8060401, 8060501,
8060601, 8100101, 8100201, 8100301, 8100401, 8111101, 8112101, 8112201, 8112301, 8113101, 8113201, 8121101, 8121201, 8122101,
8122201, 8122301, 8123101, 8123201, 8131101, 8131201, 8131301, 20100001, 30002061, 30301001, 30301002, 30301003, 30301004, 30301005,
30301006, 30301007, 30401001, 30401002, 30401003, 30401004, 30401005, 30500001, 30501001, 30502001, 30521001, 30523001, 30524001, 30525001,
30527001, 30530001, 30532001, 40102001, 40102002, 40102003, 40102004, 40103001, 40104001, 40104002, 40104003, 40104004, 40104005, 40104006,
40104007, 40104008, 40104009, 40104010, 40104011, 40104012, 40104013, 40104014, 40104015, 40104016, 40104017, 40104018, 40104019, 40104020,
40104021, 40104022, 40104023, 40104024, 40105001, 40105002, 40105003, 40105004, 40106001, 40107001, 40107002, 40121001, 40121002, 40121003,
40121005, 40121006, 40131001, 40131002, 40131003, 40211001, 40211002, 40211003, 40212001, 40212002, 40212003, 40221001, 40222001, 40222002,
40222003, 40222004, 40222005, 40231001, 40231003, 40231004, 40232001, 40232002, 40233001, 40234001, 40235001, 40236001, 40236002, 40237001,
40237002, 40237003, 40237004, 40237005, 40237006, 40238001, 40239001, 40239002, 40242001, 40242002, 40243001, 40301001, 40301002, 40302001,
40312001, 40401001, 40401002, 40401003, 40402001, 40402002, 40403001, 40403002, 40403003, 40403004, 40404001, 40411001, 40412001, 40413001,
40414001, 40416001, 40421001, 40422001, 40424001, 41000001, 41000002, 41000003, 41000004, 41000005, 41000006, 41000007, 41000008, 41000009,
41000010, 41000011, 41000012, 41212001, 41221001, 41221002, 41221003, 41401001, 41401002, 41401003, 41401004, 41401005, 41401006, 41401007,
41401008, 41401009, 41401010, 41401011, 41401012, 41401013, 41401014, 41401015, 41401016, 41401017, 41401018, 41401019, 41401020, 41402001,
43001001, 43001002, 43002001, 43002002, 43002003, 43002004, 43003001, 43004001, 43101001, 43101002, 43102001, 43102002, 43102003, 43103001,
43103002, 43103003, 43103004, 43103005, 43104001, 43201001, 43201002, 43201003, 43201004, 43201005, 43202001, 43202002, 43202003, 43202004,
43202005, 43202006, 43203001, 43204001, 43205001, 43206001, 43207001, 43207002, 43207003, 43208001, 43209001, 43210001, 43210002, 43211001,
43212001, 43213001, 43214001, 43215001, 43216001, 43217001, 43217002, 43218001, 43301001, 43301101, 43302001, 43303001, 43303002, 43303003,
43304001, 43305001, 43305002, 43306001, 43306002, 43307001, 43308001, 43309001, 43310001, 43312001, 43312002, 43312003, 43313001, 43313002,
43313003, 43313004, 43314001, 43315001, 43316001, 43321001, 43321002, 43322001, 43322002, 43322003, 43322004, 43322005, 44001001, 44001002,
44001003, 44201001, 44201002, 44201003, 44202001, 44202002, 44202003, 44202004, 44301001, 44301002, 44301003, 44301004, 44301005, 44301006,
44301007, 44301008, 44301009, 44301010, 44301011, 44301012, 44301013, 44301014, 44301015, 44301016, 44301017, 44301018, 44301019, 44301020,
44301021, 44301022, 44302001, 44302002, 44302003, 44302004, 44303001, 44303002, 44303003, 44305001, 44305002, 44305003, 44305004, 44305005,
100000102, 100000103, 100000104, 100000105, 100000301, 100000302, 100000303, 100000351, 101010102, 101010103, 101010104, 101010105, 101010106, 101010107,
101010108, 101010109, 101010110, 101010111, 101010112, 101010402, 101010502, 101010503, 101020102, 101020103, 101020104, 101020105, 101020106, 101020107,
102010102, 102010103, 102010104, 102010105, 102010106, 102020102, 102020103, 102020104, 102020105, 102020106, 102020107, 102020402, 102020403, 102020404,
104010102, 200010102, 200010103, 200010104, 200020102, 200020103, 200030102, 200040102, 201010102, 201110102, 201210102, 201220102, 201220103, 201230102,
201310102, 201310103, 201320102, 202110102, 202210102, 202210103, 202220102, 202220103, 202230102, 202310102, 202310103, 202310104, 202320102, 202420102,
202420103, 801120102,
};

246
gameserver/src/handlers.zig Normal file
View file

@ -0,0 +1,246 @@
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.onGetBigData },
.{ CmdID.CmdGetArchiveDataCsReq, avatar.onGetArchiveData },
//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 },
//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.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
View 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();
}

View file

@ -0,0 +1,243 @@
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 API = @import("../api.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");
const AllAvatars = try API.AvatarList(self.allocator, null);
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);
}
}
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, AllAvatars.items)) { //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;
}

View 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;
}
};

View 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;
}
};
}

View file

@ -0,0 +1,266 @@
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 scene_info.plane_id != 10403 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,
});
}
}
}
}
}
};

View 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);
}

View 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 API = @import("../api.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 AllAvatars = try API.AvatarList(allocator, null);
const req = try packet.getProto(protocol.GetAvatarDataCsReq, allocator);
var rsp = protocol.GetAvatarDataScRsp.init(allocator);
rsp.is_get_all = req.is_get_all;
for (AllAvatars.items) |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 onGetBigData(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;
try session.send(CmdID.CmdGetBigDataAllRecommendScRsp, rsp);
}
pub fn onGetArchiveData(session: *Session, _: *const Packet, allocator: Allocator) !void {
var rsp = protocol.GetArchiveDataScRsp.init(allocator);
var archive = protocol.ArchiveData.init(allocator);
try archive.archive_equipment_id_list.append(1);
rsp.retcode = 0;
rsp.archive_data = archive;
try session.send(CmdID.CmdGetArchiveDataScRsp, rsp);
}

View 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,
});
}

View 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/test.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(20421, 20421001, 2042101, 1027);
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,
});
}

View file

@ -0,0 +1,115 @@
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 = "CastoriceSR" },
.level = 70,
.uid = 2000,
.head_icon = 200136,
.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[2] == 47) {
msg_text2 = packet.body[2 .. packet.body.len - 8];
}
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);
}

View 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;
}
}

View 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);
}

View file

@ -0,0 +1,252 @@
const std = @import("std");
const protocol = @import("protocol");
const Session = @import("../Session.zig");
const Packet = @import("../Packet.zig");
const Data = @import("../data.zig");
const API = @import("../api.zig");
const GachaData = @import("../commands/test.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,
.goods_list = 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 const GachaDataCache = struct {
avatar_list: std.ArrayList(u32),
lightcone_list_3: std.ArrayList(u32),
lightcone_list_4: std.ArrayList(u32),
pub fn init(allocator: std.mem.Allocator) !GachaDataCache {
return .{
.avatar_list = try API.AvatarList(allocator, "CombatPowerAvatarRarityType4"),
.lightcone_list_3 = try API.LightconeList(allocator, "CombatPowerLightconeRarity3"),
.lightcone_list_4 = try API.LightconeList(allocator, "CombatPowerLightconeRarity4"),
};
}
pub fn deinit(self: *GachaDataCache) void {
self.avatar_list.deinit();
self.lightcone_list_3.deinit();
self.lightcone_list_4.deinit();
}
};
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
var gacha_data_cache: ?GachaDataCache = null;
fn getGachaData() !*GachaDataCache {
if (gacha_data_cache == null) {
gacha_data_cache = try GachaDataCache.init(arena.allocator());
}
return &gacha_data_cache.?;
}
pub fn onDoGacha(session: *Session, packet: *const Packet, allocator: std.mem.Allocator) !void {
const gacha_data = try getGachaData();
const AvatarList = gacha_data.avatar_list;
const LightconeList_3 = gacha_data.lightcone_list_3;
const LightconeList_4 = gacha_data.lightcone_list_4;
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 = LightconeList_3.items;
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 = AvatarList.items;
} else {
selected_banner = LightconeList_4.items;
}
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);
}

View 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;
}
};
}

View 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,
});
}

View file

@ -0,0 +1,80 @@
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,
};
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);
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,
});
}

View 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);
}

View file

@ -0,0 +1,28 @@
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 downloadDataBin = "CDMQuQoa1AFDUy5Vbml0eUVuZ2luZS5HYW1lT2JqZWN0LkZpbmQoIlVJUm9vdC9BYm92ZURpYWxvZy9CZXRhSGludERpYWxvZyhDbG9uZSkiKTpHZXRDb21wb25lbnRJbkNoaWxkcmVuKHR5cGVvZihDUy5SUEcuQ2xpZW50LkxvY2FsaXplZFRleHQpKS50ZXh0ID0gIkNhc3RvcmljZVNSIGlzIGZyZWUgYW5kIG9wZW4tc291cmNlIGh0dHBzOi8vZGlzY29yZC5nZy9yZXZlcnNlZHJvb21zIg==";
const size = try B64Decoder.calcSizeForSlice(downloadDataBin);
const buf = try allocator.alloc(u8, size);
_ = try B64Decoder.decode(buf, downloadDataBin);
const data = try protocol.ClientDownloadData.decode(buf, allocator);
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);
}

View 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);
}

View 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);
}

View 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,
});
}

View file

@ -0,0 +1,103 @@
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;
try rsp.owned_chat_bubbles.appendSlice(&Data.OwnedChatBubbles);
try rsp.owned_phone_themes.appendSlice(&Data.OwnedPhoneThemes);
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.signature = .{ .Const = "" };
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);
}
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);
}

View 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,
};
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,
};
}

View file

@ -0,0 +1,203 @@
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(20421, 20421001, 2042101, 1027);
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);
var rsp = protocol.GetSceneMapInfoScRsp.init(allocator);
const res_config = try Res_config.anchorLoader(allocator, "resources/res.json");
rsp.retcode = 0;
rsp.content_id = req.content_id;
rsp.entry_story_line_id = req.entry_story_line_id;
const ranges = [_][2]usize{
.{ 0, 101 },
.{ 10000, 10051 },
.{ 20000, 20001 },
.{ 30000, 30001 },
};
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 },
};
var map_info = protocol.SceneMapInfo.init(allocator);
try map_info.chest_list.ensureTotalCapacity(chest_list.len);
try map_info.unlock_teleport_list.ensureTotalCapacity(100);
try map_info.maze_prop_list.ensureTotalCapacity(100);
try map_info.maze_group_list.ensureTotalCapacity(100);
try map_info.lighten_section_list.ensureTotalCapacity(200);
for (req.floor_id_list.items) |floor_id| {
map_info.retcode = 0;
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.maze_group_list.append(protocol.MazeGroup{
.NOBKEONAKLE = std.ArrayList(u32).init(allocator),
.group_id = propConf.groupId,
});
}
}
try rsp.scene_map_info.append(map_info);
break;
}
try session.send(CmdID.CmdGetSceneMapInfoScRsp, rsp);
}
pub fn onGetUnlockTeleport(session: *Session, _: *const Packet, allocator: Allocator) !void {
var rsp = protocol.GetUnlockTeleportScRsp.init(allocator);
try rsp.unlock_teleport_list.appendSlice(&Data.TeleportList);
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,
});
}

18
protocol/build.zig Normal file
View 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"));
}

15
protocol/build.zig.zon Normal file
View file

@ -0,0 +1,15 @@
.{
.name = "protocol",
.version = "0.0.0",
.dependencies = .{
.protobuf = .{
.url = "https://github.com/Arwalk/zig-protobuf/archive/7c49ed66e029c9c7e6253b3d6d256118745550a4.tar.gz",
.hash = "122063ee7ff32a3c1aefd91a46a9fc23df0571949c3a02e2f44d39afbad0b53018a3",
},
},
.paths = .{
"build.zig",
"build.zig.zon",
"src",
},
}

48396
protocol/src/protocol.pb.zig Normal file

File diff suppressed because it is too large Load diff

1880
protocol/src/root.zig Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

6472
resources/MapEntrance.json Normal file

File diff suppressed because it is too large Load diff

4140
resources/MazePlane.json Normal file

File diff suppressed because it is too large Load diff

419269
resources/res.json Normal file

File diff suppressed because it is too large Load diff