Compare commits
4 Commits
main
...
character-
Author | SHA1 | Date | |
---|---|---|---|
|
8f3cd0e0ee | ||
|
cc91edc7e5 | ||
|
4a7eaada01 | ||
|
21ad2644c6 |
@ -1,3 +1,3 @@
|
|||||||
# CrepeSR
|
# CrepeSR
|
||||||
|
|
||||||
[Discord](https://discord.gg/KA4HqktWYG)
|
[Discord](https://discord.gg/sCAC282C)
|
||||||
|
20
package-lock.json
generated
20
package-lock.json
generated
@ -11,9 +11,7 @@
|
|||||||
"express": "^4.18.1",
|
"express": "^4.18.1",
|
||||||
"mongodb": "^4.8.0",
|
"mongodb": "^4.8.0",
|
||||||
"node-kcp-token": "github:memetrollsxd/node-kcp",
|
"node-kcp-token": "github:memetrollsxd/node-kcp",
|
||||||
"protobufjs": "^7.0.0",
|
"protobufjs": "^7.0.0"
|
||||||
"rcon-server": "^0.1.1",
|
|
||||||
"typescript": "^4.7.4"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"ts-node-dev": "^2.0.0"
|
"ts-node-dev": "^2.0.0"
|
||||||
@ -1203,11 +1201,6 @@
|
|||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/rcon-server": {
|
|
||||||
"version": "0.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/rcon-server/-/rcon-server-0.1.1.tgz",
|
|
||||||
"integrity": "sha512-oJ9+s0yxGc7in6GBBBxFXKRY2OyIVxFsoAEHlo5iFXDCuK6O4gY4qJ9XndMBVmA+nABkyOrN6QAFzoE6wZiI7A=="
|
|
||||||
},
|
|
||||||
"node_modules/readdirp": {
|
"node_modules/readdirp": {
|
||||||
"version": "3.6.0",
|
"version": "3.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
|
||||||
@ -1578,6 +1571,8 @@
|
|||||||
"version": "4.7.4",
|
"version": "4.7.4",
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz",
|
||||||
"integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==",
|
"integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==",
|
||||||
|
"dev": true,
|
||||||
|
"peer": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"tsc": "bin/tsc",
|
"tsc": "bin/tsc",
|
||||||
"tsserver": "bin/tsserver"
|
"tsserver": "bin/tsserver"
|
||||||
@ -2574,11 +2569,6 @@
|
|||||||
"unpipe": "1.0.0"
|
"unpipe": "1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"rcon-server": {
|
|
||||||
"version": "0.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/rcon-server/-/rcon-server-0.1.1.tgz",
|
|
||||||
"integrity": "sha512-oJ9+s0yxGc7in6GBBBxFXKRY2OyIVxFsoAEHlo5iFXDCuK6O4gY4qJ9XndMBVmA+nABkyOrN6QAFzoE6wZiI7A=="
|
|
||||||
},
|
|
||||||
"readdirp": {
|
"readdirp": {
|
||||||
"version": "3.6.0",
|
"version": "3.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
|
||||||
@ -2833,7 +2823,9 @@
|
|||||||
"typescript": {
|
"typescript": {
|
||||||
"version": "4.7.4",
|
"version": "4.7.4",
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz",
|
||||||
"integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ=="
|
"integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==",
|
||||||
|
"dev": true,
|
||||||
|
"peer": true
|
||||||
},
|
},
|
||||||
"unpipe": {
|
"unpipe": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
|
@ -12,8 +12,6 @@
|
|||||||
"express": "^4.18.1",
|
"express": "^4.18.1",
|
||||||
"mongodb": "^4.8.0",
|
"mongodb": "^4.8.0",
|
||||||
"node-kcp-token": "github:memetrollsxd/node-kcp",
|
"node-kcp-token": "github:memetrollsxd/node-kcp",
|
||||||
"protobufjs": "^7.0.0",
|
"protobufjs": "^7.0.0"
|
||||||
"rcon-server": "^0.1.1",
|
|
||||||
"typescript": "^4.7.4"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
2
proxy.py
2
proxy.py
@ -18,7 +18,7 @@ def next_layer(nextlayer: layer.NextLayer):
|
|||||||
f"{nextlayer.data_client()[:70]=}\n"
|
f"{nextlayer.data_client()[:70]=}\n"
|
||||||
)
|
)
|
||||||
sni = nextlayer.context.client.sni
|
sni = nextlayer.context.client.sni
|
||||||
if nextlayer.context.client.tls and sni and (sni.endswith("yuanshen.com") or sni.endswith("mihoyo.com") or sni.endswith("hoyoverse.com") or sni.endswith("starrails.com") or sni.endswith("bhsr.com")):
|
if nextlayer.context.client.tls and sni and (sni.endswith("yuanshen.com") or sni.endswith("mihoyo.com") or sni.endswith("hoyoverse.com") or sni.endswith("starrails.com")):
|
||||||
ctx.log('sni:' + sni)
|
ctx.log('sni:' + sni)
|
||||||
nextlayer.context.server.address = ("127.0.0.1", 443)
|
nextlayer.context.server.address = ("127.0.0.1", 443)
|
||||||
|
|
||||||
|
@ -23,18 +23,26 @@ export default class Interface {
|
|||||||
output: process.stdout
|
output: process.stdout
|
||||||
});
|
});
|
||||||
|
|
||||||
public static target?: Session;
|
public static target: Session;
|
||||||
|
|
||||||
private constructor() { }
|
private constructor() { }
|
||||||
|
|
||||||
public static start() {
|
public static readonly start = () => {
|
||||||
Interface.rl.question("", (_command) => {
|
Interface.rl.question("", (_command) => {
|
||||||
if (!_command) {
|
if (!_command) {
|
||||||
Interface.start();
|
Interface.start();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const cmd = new Command(_command);
|
const cmd = new Command(_command);
|
||||||
Interface.handle(cmd);
|
import(`./${alias[cmd.name] || cmd.name}`).then(async module => {
|
||||||
|
await module.default(cmd);
|
||||||
|
}).catch(err => {
|
||||||
|
if (err.code == "MODULE_NOT_FOUND") {
|
||||||
|
c.log(`Command ${cmd.name} not found.`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
c.error(err);
|
||||||
|
});
|
||||||
Interface.start();
|
Interface.start();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -43,16 +51,4 @@ export default class Interface {
|
|||||||
process.exit(0);
|
process.exit(0);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static handle(cmd: Command) {
|
|
||||||
import(`./${alias[cmd.name] || cmd.name}`).then(async module => {
|
|
||||||
await module.default(cmd);
|
|
||||||
}).catch(err => {
|
|
||||||
if (err.code == "MODULE_NOT_FOUND") {
|
|
||||||
c.log(`Command ${cmd.name} not found.`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
c.error(err);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -1,17 +0,0 @@
|
|||||||
import Logger from "../util/Logger";
|
|
||||||
import Interface, { Command } from "./Interface";
|
|
||||||
const c = new Logger("/ban", "blue");
|
|
||||||
|
|
||||||
export default async function handle(command: Command) {
|
|
||||||
if (!Interface.target) {
|
|
||||||
c.log("No target specified");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const banStatus = Interface.target.player.db.banned;
|
|
||||||
Interface.target.player.db.banned = !banStatus;
|
|
||||||
Interface.target.player.save();
|
|
||||||
Interface.target.kick();
|
|
||||||
|
|
||||||
c.log(`${banStatus ? "Banned" : "Unbanned"} ${Interface.target.account.name}`);
|
|
||||||
}
|
|
@ -1,68 +0,0 @@
|
|||||||
import { BattleAvatar, BattleBuff, HeroPath, Item, ItemList, SceneBattleInfo, SceneMonsterWave, StartCocoonStageScRsp } from "../data/proto/StarRail";
|
|
||||||
import Avatar from "../db/Avatar";
|
|
||||||
import Inventory from "../db/Inventory";
|
|
||||||
import Logger from "../util/Logger";
|
|
||||||
import Interface, { Command } from "./Interface";
|
|
||||||
const c = new Logger("/battle", "blue");
|
|
||||||
|
|
||||||
export default async function handle(command: Command) {
|
|
||||||
if (!Interface.target) {
|
|
||||||
c.log("No target specified");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (command.args.length == 0) {
|
|
||||||
c.log("Usage: /battle <monsterId>+");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const player = Interface.target.player;
|
|
||||||
if (player.db.posData.planeID != 20101) {
|
|
||||||
c.log("For now, this is only confirmed to work on plane 20101, please teleport there first.");
|
|
||||||
}
|
|
||||||
|
|
||||||
const inventory = await player.getInventory();
|
|
||||||
const avatars = await Avatar.getAvatarsForLineup(player, player.db.lineup.lineups[player.db.lineup.curIndex]);
|
|
||||||
const monsters = command.args.map(arg => Number(arg));
|
|
||||||
|
|
||||||
Interface.target.send(StartCocoonStageScRsp, {
|
|
||||||
retcode: 0,
|
|
||||||
propEntityId: 0,
|
|
||||||
cocoonId: 1001,
|
|
||||||
wave: 0,
|
|
||||||
battleInfo: {
|
|
||||||
logicRandomSeed: 1,
|
|
||||||
stageId: 1022010,
|
|
||||||
monsterWaveList: [
|
|
||||||
{ monsterIdList: monsters, dropList: [{ itemList: [{ itemId: 102, num: 10 } as Item] } as ItemList] } as SceneMonsterWave
|
|
||||||
],
|
|
||||||
battleAvatarList: avatars.map((avatar, i) => {
|
|
||||||
const equipment = inventory.getEquipmentByUid(avatar.db.equipmentUniqueId);
|
|
||||||
const equipData = (equipment) ? [{
|
|
||||||
id: equipment.tid,
|
|
||||||
level: equipment.level,
|
|
||||||
promotion: equipment.promotion,
|
|
||||||
rank: equipment.rank
|
|
||||||
}] : [];
|
|
||||||
|
|
||||||
return {
|
|
||||||
avatarType: avatar.db.avatarType,
|
|
||||||
equipmentList: equipData,
|
|
||||||
hp: avatar.db.fightProps.hp,
|
|
||||||
id: avatar.db.baseAvatarId,
|
|
||||||
sp: avatar.db.fightProps.sp,
|
|
||||||
index: i,
|
|
||||||
level: avatar.db.level,
|
|
||||||
promotion: avatar.db.promotion,
|
|
||||||
rank: avatar.db.rank,
|
|
||||||
relicList: [],
|
|
||||||
skilltreeList: []
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
buffList: new Array<BattleBuff>(),
|
|
||||||
battleId: 0,
|
|
||||||
heroPathList: new Array<HeroPath>(),
|
|
||||||
roundsLimit: 100
|
|
||||||
} as SceneBattleInfo
|
|
||||||
} as StartCocoonStageScRsp);
|
|
||||||
}
|
|
@ -1,14 +0,0 @@
|
|||||||
import SRServer from "../server/kcp/SRServer";
|
|
||||||
import Logger from "../util/Logger";
|
|
||||||
import { Command } from "./Interface";
|
|
||||||
const c = new Logger("/kickall", "blue");
|
|
||||||
|
|
||||||
export default async function handle(command: Command) {
|
|
||||||
const hard = command.args[0];
|
|
||||||
|
|
||||||
for (const [key, session] of SRServer.getInstance().sessions) {
|
|
||||||
session.kick(!!hard);
|
|
||||||
}
|
|
||||||
|
|
||||||
c.log(`Kicked all players. Hard kick: ${!!hard}`);
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
import Logger from "../util/Logger";
|
|
||||||
import Interface, { Command } from "./Interface";
|
|
||||||
const c = new Logger("/pos", "blue");
|
|
||||||
|
|
||||||
export default async function handle(command: Command) {
|
|
||||||
if (!Interface.target) {
|
|
||||||
c.log("No target specified");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const pos = Interface.target.player.db.posData.pos;
|
|
||||||
c.log(`Current position: x=${pos.x}, y=${pos.y}, z=${pos.z}.`);
|
|
||||||
}
|
|
@ -3,7 +3,6 @@ import { ActorEntity } from "../game/entities/Actor";
|
|||||||
import Interface, { Command } from "./Interface";
|
import Interface, { Command } from "./Interface";
|
||||||
import { GetCurSceneInfoScRsp } from "../data/proto/StarRail";
|
import { GetCurSceneInfoScRsp } from "../data/proto/StarRail";
|
||||||
import MazePlaneExcel from "../util/excel/MazePlaneExcel";
|
import MazePlaneExcel from "../util/excel/MazePlaneExcel";
|
||||||
import MapEntryExcel from "../util/excel/MapEntryExcel";
|
|
||||||
const c = new Logger("/scene", "blue");
|
const c = new Logger("/scene", "blue");
|
||||||
|
|
||||||
export default async function handle(command: Command) {
|
export default async function handle(command: Command) {
|
||||||
@ -12,8 +11,8 @@ export default async function handle(command: Command) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const planeID = MazePlaneExcel.fromPlaneId(parseInt(command.args[0]));
|
const planeID = MazePlaneExcel.fromPlaneId(parseInt(command.args[0]))
|
||||||
const entryId = MapEntryExcel.fromFloorId(planeID.StartFloorID).ID;
|
const uid = Interface.target.player.db._id;
|
||||||
const posData = Interface.target.player.db.posData;
|
const posData = Interface.target.player.db.posData;
|
||||||
|
|
||||||
const lineup2 = await Interface.target.player.getLineup();
|
const lineup2 = await Interface.target.player.getLineup();
|
||||||
@ -36,9 +35,9 @@ export default async function handle(command: Command) {
|
|||||||
curAvatarEntity
|
curAvatarEntity
|
||||||
],
|
],
|
||||||
entityBuffList: [],
|
entityBuffList: [],
|
||||||
entryId: entryId,
|
entryId: 10001,
|
||||||
envBuffList: [],
|
envBuffList: [],
|
||||||
gameModeType: MazePlaneExcel.getGameModeForPlaneType(planeID.PlaneType),
|
gameModeType: 1,
|
||||||
lightenSectionList: []
|
lightenSectionList: []
|
||||||
},
|
},
|
||||||
} as unknown as GetCurSceneInfoScRsp);
|
} as unknown as GetCurSceneInfoScRsp);
|
||||||
|
@ -15,27 +15,26 @@ export default async function handle(command: Command) {
|
|||||||
|
|
||||||
SRServer.getInstance().sessions.forEach(client => {
|
SRServer.getInstance().sessions.forEach(client => {
|
||||||
possibleTargets.push({
|
possibleTargets.push({
|
||||||
id: `${client.ctx.address}:${client.ctx.port} (UID: ${client.account.uid})`,
|
id: `${client.ctx.address}:${client.ctx.port}`,
|
||||||
uid: Number(client.account.uid),
|
uid: client.player.uid,
|
||||||
session: client
|
session: client
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!target) {
|
if (!target) {
|
||||||
c.log("No target specified");
|
c.log("No target specified");
|
||||||
if (Interface.target) c.log(`Current target: ${Interface.target.account.name} (UID: ${Interface.target.account.uid})`);
|
|
||||||
c.log("Possible targets: ");
|
c.log("Possible targets: ");
|
||||||
possibleTargets.forEach(x => c.trail(`${x.id} (UID: ${x.uid})`));
|
possibleTargets.forEach(x => c.trail(`${x.id} (UID: ${x.uid})`));
|
||||||
if (!possibleTargets[1] && possibleTargets[0]) {
|
if(!possibleTargets[1] && possibleTargets[0]){
|
||||||
c.log(`Auto targetting the only session ${possibleTargets[0].uid}`);
|
c.log(`Auto target the only session ${possibleTargets[0].uid}`);
|
||||||
Interface.target = possibleTargets[0].session;
|
Interface.target = possibleTargets[0].session;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const autoTarget = findBestMatch(target, possibleTargets.map(x => x.id))?.bestMatch?.target;
|
const autoTarget = findBestMatch(target, possibleTargets.map(x => x.id))?.bestMatch.target;
|
||||||
|
|
||||||
Interface.target = possibleTargets.find(x => x.id === autoTarget)?.session;
|
Interface.target = possibleTargets.find(x => x.id === autoTarget)!.session;
|
||||||
|
|
||||||
c.log(`Target set to ${Interface.target ? Interface.target.account.name : "none"} (UID: ${Interface.target ? Interface.target.account.uid : "none"})`);
|
c.log(`Target set to ${autoTarget}`);
|
||||||
}
|
}
|
@ -119,15 +119,6 @@ export default class Inventory {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Fetch the equipment with the given unique ID from the player's inventory.
|
|
||||||
* @param uniqueId The unique ID of the equipment to fetch.
|
|
||||||
* @returns The `Equipment` with the given unique ID, or `undefined` if the player does not have that equipment.
|
|
||||||
*/
|
|
||||||
public getEquipmentByUid(uniqueId: number) {
|
|
||||||
return this.db.equipments.filter(e => e.uniqueId == uniqueId)?.[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
/********************************************************************************
|
/********************************************************************************
|
||||||
Add items to the inventory.
|
Add items to the inventory.
|
||||||
********************************************************************************/
|
********************************************************************************/
|
||||||
@ -385,28 +376,17 @@ export default class Inventory {
|
|||||||
/********************************************************************************
|
/********************************************************************************
|
||||||
Player updating.
|
Player updating.
|
||||||
********************************************************************************/
|
********************************************************************************/
|
||||||
/**
|
private sendMaterialUpdate() {
|
||||||
* Send `PlayerSyncScNotify` for materials.
|
|
||||||
*/
|
|
||||||
public sendMaterialUpdate() {
|
|
||||||
this.player.session.send(PlayerSyncScNotify, PlayerSyncScNotify.fromPartial({
|
this.player.session.send(PlayerSyncScNotify, PlayerSyncScNotify.fromPartial({
|
||||||
materialList: this.getMaterialList()
|
materialList: this.getMaterialList()
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
private sendEquipmentUpdate() {
|
||||||
/**
|
|
||||||
* Send `PlayerSyncScNotify` for equipments.
|
|
||||||
*/
|
|
||||||
public sendEquipmentUpdate() {
|
|
||||||
this.player.session.send(PlayerSyncScNotify, PlayerSyncScNotify.fromPartial({
|
this.player.session.send(PlayerSyncScNotify, PlayerSyncScNotify.fromPartial({
|
||||||
equipmentList: this.getEquipmentList()
|
equipmentList: this.getEquipmentList()
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
private sendRelicUpdate() {
|
||||||
/**
|
|
||||||
* Send `PlayerSyncScNotify` for relics.
|
|
||||||
*/
|
|
||||||
public sendRelicUpdate() {
|
|
||||||
this.player.session.send(PlayerSyncScNotify, PlayerSyncScNotify.fromPartial({
|
this.player.session.send(PlayerSyncScNotify, PlayerSyncScNotify.fromPartial({
|
||||||
relicList: this.getRelicsList()
|
relicList: this.getRelicsList()
|
||||||
}));
|
}));
|
||||||
|
@ -13,11 +13,9 @@ export class Scene {
|
|||||||
public spawnEntity(entity: Entity, silent: boolean = false) {
|
public spawnEntity(entity: Entity, silent: boolean = false) {
|
||||||
this.entities.set(entity.entityId, entity);
|
this.entities.set(entity.entityId, entity);
|
||||||
if (!silent) {
|
if (!silent) {
|
||||||
const dataObj : SceneEntityUpdateScNotify = {
|
this.player.session.send(SceneEntityUpdateScNotify, {
|
||||||
entityList: [entity.getSceneEntityInfo()]
|
entityList: [entity.getSceneEntityInfo()]
|
||||||
};
|
} as SceneEntityUpdateScNotify);
|
||||||
|
|
||||||
this.player.session.send(SceneEntityUpdateScNotify, dataObj);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,16 +10,15 @@ export class ActorEntity extends Entity
|
|||||||
super(scene, pos, rot);
|
super(scene, pos, rot);
|
||||||
}
|
}
|
||||||
|
|
||||||
public getSceneActorInfo(): SceneEntityInfo {
|
public getSceneEntityInfo(): SceneEntityInfo {
|
||||||
return {
|
const sceneEntityInfo = super.getSceneEntityInfo();
|
||||||
...super.getSceneEntityInfo(),
|
sceneEntityInfo.actor = {
|
||||||
actor: {
|
avatarType: AvatarType.AVATAR_FORMAL_TYPE,
|
||||||
avatarType: AvatarType.AVATAR_FORMAL_TYPE,
|
baseAvatarId: this.avatarId,
|
||||||
baseAvatarId: this.avatarId,
|
uid: this.scene.player.db._id,
|
||||||
uid: this.scene.player.db._id,
|
mapLayer: 0 //?
|
||||||
mapLayer: 0 //?
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
return sceneEntityInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
public getEntityType(): EntityType {
|
public getEntityType(): EntityType {
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
import Interface from "./commands/Interface";
|
import Interface from "./commands/Interface";
|
||||||
import HttpServer from "./http/HttpServer";
|
import HttpServer from "./http/HttpServer";
|
||||||
import SRServer from "./server/kcp/SRServer";
|
import SRServer from "./server/kcp/SRServer";
|
||||||
import RCONServer from "./server/rcon/RCONServer";
|
|
||||||
import Banners from "./util/Banner";
|
import Banners from "./util/Banner";
|
||||||
import Logger from "./util/Logger";
|
import Logger from "./util/Logger";
|
||||||
import ProtoFactory from "./util/ProtoFactory"
|
import ProtoFactory from "./util/ProtoFactory"
|
||||||
@ -19,4 +18,3 @@ ProtoFactory.init();
|
|||||||
Interface.start();
|
Interface.start();
|
||||||
HttpServer.getInstance().start();
|
HttpServer.getInstance().start();
|
||||||
SRServer.getInstance().start();
|
SRServer.getInstance().start();
|
||||||
RCONServer.getInstance().start();
|
|
@ -1,15 +1,11 @@
|
|||||||
import Logger, { VerboseLevel } from "../../util/Logger";
|
import Logger, { VerboseLevel } from "../../util/Logger";
|
||||||
import protobuf, { Root } from 'protobufjs';
|
import protobuf, { Root } from 'protobufjs';
|
||||||
import { resolve } from 'path';
|
import { resolve } from 'path';
|
||||||
import ProtoFactory from "../../util/ProtoFactory";
|
|
||||||
const c = new Logger("Packet")
|
const c = new Logger("Packet")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export default class Packet {
|
export default class Packet {
|
||||||
public readonly cmdid: number;
|
public readonly cmdid: number;
|
||||||
public readonly data: Buffer;
|
public readonly data: Buffer;
|
||||||
|
private static root: Root = Packet.getRoot();
|
||||||
public body: {} = {};
|
public body: {} = {};
|
||||||
|
|
||||||
public constructor(public readonly rawData: Buffer, public readonly protoName: string = "") {
|
public constructor(public readonly rawData: Buffer, public readonly protoName: string = "") {
|
||||||
@ -19,10 +15,9 @@ export default class Packet {
|
|||||||
this.cmdid = this.rawData.readUInt16BE(4);
|
this.cmdid = this.rawData.readUInt16BE(4);
|
||||||
|
|
||||||
this.protoName = this.protoName || CmdID[this.cmdid];
|
this.protoName = this.protoName || CmdID[this.cmdid];
|
||||||
|
if (this.protoName) {
|
||||||
if(this.protoName){
|
|
||||||
try {
|
try {
|
||||||
const Message = ProtoFactory.getType(this.protoName as PacketName);
|
const Message = Packet.root.lookupTypeOrEnum(this.protoName);
|
||||||
this.body = Message.decode(this.data);
|
this.body = Message.decode(this.data);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
c.warn(`Failed to decode ${this.protoName}`);
|
c.warn(`Failed to decode ${this.protoName}`);
|
||||||
@ -42,6 +37,37 @@ export default class Packet {
|
|||||||
return str.startsWith("01234567") && str.endsWith("89abcdef");
|
return str.startsWith("01234567") && str.endsWith("89abcdef");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static encode(name: PacketName, body: {}, customCmdId?: number): Packet | null {
|
||||||
|
try {
|
||||||
|
const cmdid = CmdID[name];
|
||||||
|
const Message = Packet.root.lookupTypeOrEnum(name);
|
||||||
|
|
||||||
|
const data = Buffer.from(Message.encode(body).finish());
|
||||||
|
const packet = Buffer.allocUnsafe(16 + data.length);
|
||||||
|
packet.writeUInt32BE(0x1234567);
|
||||||
|
packet.writeUint16BE(customCmdId || cmdid, 4);
|
||||||
|
packet.writeUint16BE(0, 6);
|
||||||
|
packet.writeUint32BE(data.length, 8);
|
||||||
|
data.copy(packet, 12);
|
||||||
|
packet.writeUint32BE(0x89abcdef, 12 + data.length);
|
||||||
|
|
||||||
|
return new Packet(packet, name);
|
||||||
|
} catch (e) {
|
||||||
|
c.error(e as Error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static getRoot(): Root {
|
||||||
|
try {
|
||||||
|
// Combined proto file with all definitions
|
||||||
|
return protobuf.loadSync(resolve(__dirname, `../../data/proto/StarRail.proto`));
|
||||||
|
} catch (e) {
|
||||||
|
c.error("Failed to load proto root! Server will not be able to function properly. Please check your data/ folder.");
|
||||||
|
c.error(e as Error, false);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static fromEncodedBuffer(data: Buffer, name: PacketName): Buffer {
|
public static fromEncodedBuffer(data: Buffer, name: PacketName): Buffer {
|
||||||
const cmdid = CmdID[name];
|
const cmdid = CmdID[name];
|
||||||
@ -54,6 +80,8 @@ export default class Packet {
|
|||||||
packet.writeUint32BE(0x89abcdef, 12 + data.length);
|
packet.writeUint32BE(0x89abcdef, 12 + data.length);
|
||||||
return packet;
|
return packet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type PacketName = keyof typeof CmdID;
|
export type PacketName = keyof typeof CmdID;
|
||||||
|
@ -80,6 +80,7 @@ export default class Session {
|
|||||||
|
|
||||||
public async sync() {
|
public async sync() {
|
||||||
const avatars = await Avatar.loadAvatarsForPlayer(this.player);
|
const avatars = await Avatar.loadAvatarsForPlayer(this.player);
|
||||||
|
//const avatars = await Avatar.fromUID(this.player.db._id);
|
||||||
const inventory = await this.player.getInventory();
|
const inventory = await this.player.getInventory();
|
||||||
|
|
||||||
this.send(PlayerSyncScNotify, PlayerSyncScNotify.fromPartial({
|
this.send(PlayerSyncScNotify, PlayerSyncScNotify.fromPartial({
|
||||||
@ -91,15 +92,17 @@ export default class Session {
|
|||||||
relicList: inventory.getRelicsList(),
|
relicList: inventory.getRelicsList(),
|
||||||
basicInfo: this.player.db.basicInfo
|
basicInfo: this.player.db.basicInfo
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
//this.player.save();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async send<Class extends MessageType<any>,>(type: Class, data: UnWrapMessageType<Class>) {
|
public async send<Class extends MessageType<any>, >(type: Class, data: UnWrapMessageType<Class>) {
|
||||||
const typeName = ProtoFactory.getName(type);
|
const typeName = ProtoFactory.getName(type);
|
||||||
const encodedBuffer = type.encode(type.fromPartial(data)).finish();
|
const encodedBuffer = type.encode(type.fromPartial(data)).finish();
|
||||||
const packet = Packet.fromEncodedBuffer(Buffer.from(encodedBuffer), typeName);
|
const packet = Packet.fromEncodedBuffer(Buffer.from(encodedBuffer), typeName);
|
||||||
this.c.verbL(data);
|
this.c.verbL(data);
|
||||||
this.c.verbH(encodedBuffer);
|
this.c.verbH(encodedBuffer);
|
||||||
if (!encodedBuffer) this.c.error("encodedBuffer is undefined");
|
if(!encodedBuffer) console.log("sad!")
|
||||||
if (Logger.VERBOSE_LEVEL >= VerboseLevel.WARNS) this.c.log(typeName);
|
if (Logger.VERBOSE_LEVEL >= VerboseLevel.WARNS) this.c.log(typeName);
|
||||||
//todo: might want to regen the ts-proto types with env = node
|
//todo: might want to regen the ts-proto types with env = node
|
||||||
this.kcpobj.send(packet);
|
this.kcpobj.send(packet);
|
||||||
@ -109,7 +112,6 @@ export default class Session {
|
|||||||
public kick(hard: boolean = true) {
|
public kick(hard: boolean = true) {
|
||||||
SRServer.getInstance().sessions.delete(this.id);
|
SRServer.getInstance().sessions.delete(this.id);
|
||||||
this.kicked = true;
|
this.kicked = true;
|
||||||
|
|
||||||
if (hard) this.send(PlayerKickOutScNotify, {
|
if (hard) this.send(PlayerKickOutScNotify, {
|
||||||
kickType: PlayerKickOutScNotify_KickType.KICK_BLACK,
|
kickType: PlayerKickOutScNotify_KickType.KICK_BLACK,
|
||||||
blackInfo: {
|
blackInfo: {
|
||||||
|
@ -65,9 +65,9 @@ export default async function handle(session: Session, packet: Packet) {
|
|||||||
|
|
||||||
// Calculate the character's new EXP and any excess EXP.
|
// Calculate the character's new EXP and any excess EXP.
|
||||||
let excessExp = 0;
|
let excessExp = 0;
|
||||||
if (avatar.db.level == levelCap) {
|
if (avatar.db.level == levelCap && currentAvatarExp >= nextRequiredExp) {
|
||||||
avatar.db.exp = 0;
|
avatar.db.exp = nextRequiredExp;
|
||||||
excessExp = currentAvatarExp;
|
excessExp = currentAvatarExp - nextRequiredExp;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
avatar.db.exp = currentAvatarExp;
|
avatar.db.exp = currentAvatarExp;
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { EnterMazeCsReq, EnterMazeScRsp } from "../../data/proto/StarRail";
|
import { EnterMazeCsReq, EnterMazeScRsp } from "../../data/proto/StarRail";
|
||||||
import MapEntryExcel from "../../util/excel/MapEntryExcel";
|
|
||||||
import MazePlaneExcel from "../../util/excel/MazePlaneExcel";
|
import MazePlaneExcel from "../../util/excel/MazePlaneExcel";
|
||||||
import Packet from "../kcp/Packet";
|
import Packet from "../kcp/Packet";
|
||||||
import Session from "../kcp/Session";
|
import Session from "../kcp/Session";
|
||||||
@ -7,8 +6,7 @@ import Session from "../kcp/Session";
|
|||||||
export default async function handle(session: Session, packet: Packet) {
|
export default async function handle(session: Session, packet: Packet) {
|
||||||
const body = packet.body as EnterMazeCsReq;
|
const body = packet.body as EnterMazeCsReq;
|
||||||
|
|
||||||
const mazeEntry = MapEntryExcel.fromId(body.entryId);
|
const mazeEntry = MazePlaneExcel.getEntry(body.entryId);
|
||||||
const mazePlane = MazePlaneExcel.fromPlaneId(mazeEntry.PlaneID);
|
|
||||||
|
|
||||||
let curLineup = await session.player.getLineup();
|
let curLineup = await session.player.getLineup();
|
||||||
curLineup.planeId = mazeEntry.PlaneID;
|
curLineup.planeId = mazeEntry.PlaneID;
|
||||||
@ -25,7 +23,7 @@ export default async function handle(session: Session, packet: Packet) {
|
|||||||
scene: {
|
scene: {
|
||||||
planeId: mazeEntry.PlaneID,
|
planeId: mazeEntry.PlaneID,
|
||||||
floorId: mazeEntry.FloorID,
|
floorId: mazeEntry.FloorID,
|
||||||
gameModeType: MazePlaneExcel.getGameModeForPlaneType(mazePlane.PlaneType),
|
gameModeType: 1,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
id: mazeEntry.PlaneID,
|
id: mazeEntry.PlaneID,
|
||||||
|
@ -1,100 +0,0 @@
|
|||||||
import { ExpUpEquipmentCsReq, ExpUpEquipmentScRsp } from "../../data/proto/StarRail";
|
|
||||||
import { PayItemData } from "../../db/Inventory";
|
|
||||||
import EquipmentExcel from "../../util/excel/EquipmentExcel";
|
|
||||||
import EquipmentExpItemExcel from "../../util/excel/EquipmentExpItemExcel";
|
|
||||||
import EquipmentExpTypeExcel from "../../util/excel/EquipmentExpTypeExcel";
|
|
||||||
import EquipmentPromotionExcel from "../../util/excel/EquipmentPromotionExcel";
|
|
||||||
import Packet from "../kcp/Packet";
|
|
||||||
import Session from "../kcp/Session";
|
|
||||||
|
|
||||||
export default async function handle(session: Session, packet: Packet) {
|
|
||||||
const body = packet.body as ExpUpEquipmentCsReq;
|
|
||||||
const inventory = await session.player.getInventory();
|
|
||||||
|
|
||||||
// Get the target equipment.
|
|
||||||
const equipmentId = body.equipmentUniqueId;
|
|
||||||
const equipment = inventory.getEquipmentByUid(equipmentId);
|
|
||||||
const equipmentExcelData = EquipmentExcel.fromId(equipment.tid);
|
|
||||||
|
|
||||||
// Determine the next level cap based on the equipment's current promotion.
|
|
||||||
const levelCap = EquipmentPromotionExcel.fromId(`${equipment.tid}:${equipment.promotion}`).MaxLevel;
|
|
||||||
|
|
||||||
// Determine the EXP we get from the consumed items, and the coins it will cost.
|
|
||||||
let exp = 0;
|
|
||||||
let cost = 0;
|
|
||||||
|
|
||||||
const costMaterialList = [];
|
|
||||||
const costEquipmentList = []
|
|
||||||
|
|
||||||
for (const item of body.costData!.itemList) {
|
|
||||||
// Determine amount of EXP given by that item.
|
|
||||||
// If the consumed item is a PileItem, we can fetch the EXP it gives from the excels.
|
|
||||||
if (item.pileItem) {
|
|
||||||
const expItemData = EquipmentExpItemExcel.fromId(item.pileItem.itemId);
|
|
||||||
exp += expItemData.ExpProvide * item.pileItem.itemNum;
|
|
||||||
cost += expItemData.CoinCost;
|
|
||||||
|
|
||||||
costMaterialList.push({ id: item.pileItem.itemId, count: item.pileItem.itemNum } as PayItemData);
|
|
||||||
}
|
|
||||||
else if (item.equipmentUniqueId) {
|
|
||||||
const consumedEquipment = inventory.getEquipmentByUid(item.equipmentUniqueId);
|
|
||||||
const consumedEquipmentExcelData = EquipmentExcel.fromId(consumedEquipment.tid);
|
|
||||||
|
|
||||||
exp += consumedEquipmentExcelData.ExpProvide;
|
|
||||||
cost += consumedEquipmentExcelData.CoinCost;
|
|
||||||
|
|
||||||
costEquipmentList.push(item.equipmentUniqueId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
costMaterialList.push({ id: 2, count: cost } as PayItemData);
|
|
||||||
|
|
||||||
// Try consuming materials.
|
|
||||||
const success = await inventory.payItems(costMaterialList);
|
|
||||||
if (!success) {
|
|
||||||
// ToDo: Correct retcode.
|
|
||||||
session.send(ExpUpEquipmentScRsp, { retcode: 1, returnItemList: [] } as ExpUpEquipmentScRsp);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const id of costEquipmentList) {
|
|
||||||
await inventory.removeEquipment(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cost has been paid - now level up.
|
|
||||||
let currentEquipmentExp = equipment.exp + exp;
|
|
||||||
let nextRequiredExp = EquipmentExpTypeExcel.fromId(`${equipmentExcelData.ExpType}:${equipment.level}`).Exp;
|
|
||||||
while (currentEquipmentExp >= nextRequiredExp && equipment.level < levelCap) {
|
|
||||||
// Increase level.
|
|
||||||
equipment.level++;
|
|
||||||
|
|
||||||
// Deduct EXP necessary for this level.
|
|
||||||
currentEquipmentExp -= nextRequiredExp;
|
|
||||||
|
|
||||||
// Determine EXP necessary for the next level.
|
|
||||||
nextRequiredExp = EquipmentExpTypeExcel.fromId(`${equipmentExcelData.ExpType}:${equipment.level}`).Exp;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate the equipment's new EXP and any excess EXP.
|
|
||||||
let excessExp = 0;
|
|
||||||
if (equipment.level == levelCap) {
|
|
||||||
equipment.exp = 0;
|
|
||||||
excessExp = currentEquipmentExp;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
equipment.exp = currentEquipmentExp;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save.
|
|
||||||
await inventory.save();
|
|
||||||
|
|
||||||
// ToDo: Handle return items.
|
|
||||||
|
|
||||||
// Done. Sync and send response.
|
|
||||||
await session.sync();
|
|
||||||
|
|
||||||
session.send(ExpUpEquipmentScRsp, {
|
|
||||||
retcode: 0,
|
|
||||||
returnItemList: []
|
|
||||||
} as ExpUpEquipmentScRsp);
|
|
||||||
}
|
|
@ -1,43 +1,28 @@
|
|||||||
import { GetCurSceneInfoScRsp, MotionInfo, SceneEntityInfo, SceneNpcMonsterInfo, StartCocoonStageCsReq, Vector } from "../../data/proto/StarRail";
|
import { GetCurSceneInfoScRsp, Vector } from "../../data/proto/StarRail";
|
||||||
import { ActorEntity } from "../../game/entities/Actor";
|
import { ActorEntity } from "../../game/entities/Actor";
|
||||||
import { PropEntity } from "../../game/entities/Prop";
|
|
||||||
import MapEntryExcel from "../../util/excel/MapEntryExcel";
|
|
||||||
import MazePlaneExcel from "../../util/excel/MazePlaneExcel";
|
|
||||||
import Packet from "../kcp/Packet";
|
import Packet from "../kcp/Packet";
|
||||||
import Session from "../kcp/Session";
|
import Session from "../kcp/Session";
|
||||||
|
|
||||||
export default async function handle(session: Session, packet: Packet) {
|
export default async function handle(session: Session, packet: Packet) {
|
||||||
// Get data.
|
|
||||||
const posData = session.player.db.posData;
|
const posData = session.player.db.posData;
|
||||||
const _lineup = session.player.db.lineup;
|
const _lineup = session.player.db.lineup;
|
||||||
const lineup = _lineup.lineups[_lineup.curIndex];
|
const lineup = _lineup.lineups[_lineup.curIndex];
|
||||||
const curAvatarEntity = new ActorEntity(session.player.scene, lineup.avatarList[lineup.leaderSlot], posData.pos);
|
const curAvatarEntity = new ActorEntity(session.player.scene, lineup.avatarList[0], posData.pos);
|
||||||
const entryId = MapEntryExcel.fromFloorId(posData.floorID).ID;
|
session.send(GetCurSceneInfoScRsp, {
|
||||||
const mazePlane = MazePlaneExcel.fromPlaneId(posData.planeID);
|
|
||||||
|
|
||||||
// Scene management.
|
|
||||||
session.player.scene.spawnEntity(curAvatarEntity, true);
|
|
||||||
session.player.scene.entryId = entryId;
|
|
||||||
|
|
||||||
// Build response.
|
|
||||||
const dataObj : GetCurSceneInfoScRsp = {
|
|
||||||
retcode: 0,
|
retcode: 0,
|
||||||
scene: {
|
scene: {
|
||||||
planeId: posData.planeID,
|
planeId: posData.planeID,
|
||||||
floorId: posData.floorID,
|
floorId: posData.floorID,
|
||||||
entityList: [
|
entityList: [
|
||||||
curAvatarEntity.getSceneEntityInfo(),
|
curAvatarEntity
|
||||||
|
|
||||||
],
|
],
|
||||||
lightenSectionList: [],
|
|
||||||
leaderEntityId: curAvatarEntity.entityId,
|
|
||||||
entityBuffList: [],
|
entityBuffList: [],
|
||||||
entryId: entryId,
|
entryId: 10001,
|
||||||
envBuffList: [],
|
envBuffList: [],
|
||||||
gameModeType: MazePlaneExcel.getGameModeForPlaneType(mazePlane.PlaneType),
|
gameModeType: 1,
|
||||||
}
|
lightenSectionList: []
|
||||||
};
|
},
|
||||||
|
} as unknown as GetCurSceneInfoScRsp);
|
||||||
// Send response.
|
session.player.scene.spawnEntity(curAvatarEntity, true);
|
||||||
session.send(GetCurSceneInfoScRsp, dataObj);
|
session.player.scene.entryId = 10001;
|
||||||
}
|
}
|
@ -1,5 +1,4 @@
|
|||||||
import { GetMazeMapInfoCsReq, GetMazeMapInfoScRsp } from "../../data/proto/StarRail";
|
import { GetMazeMapInfoCsReq, GetMazeMapInfoScRsp } from "../../data/proto/StarRail";
|
||||||
import MapEntryExcel from "../../util/excel/MapEntryExcel";
|
|
||||||
import MappingInfoExcel from "../../util/excel/MappingInfoExcel";
|
import MappingInfoExcel from "../../util/excel/MappingInfoExcel";
|
||||||
import MazePlaneExcel from "../../util/excel/MazePlaneExcel";
|
import MazePlaneExcel from "../../util/excel/MazePlaneExcel";
|
||||||
import Packet from "../kcp/Packet";
|
import Packet from "../kcp/Packet";
|
||||||
@ -25,7 +24,7 @@ export default async function handle(session: Session, packet: Packet) {
|
|||||||
dataObj.lightenSectionList.push(i)
|
dataObj.lightenSectionList.push(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
dataObj.unlockTeleportList = MapEntryExcel.all().map(x => x.ID);
|
dataObj.unlockTeleportList = MazePlaneExcel.getAllEntries().map(x => x.ID);
|
||||||
|
|
||||||
session.send(GetMazeMapInfoScRsp, dataObj);
|
session.send(GetMazeMapInfoScRsp, dataObj);
|
||||||
}
|
}
|
@ -1,26 +1,20 @@
|
|||||||
import {
|
import { AvatarType, JoinLineupCsReq, JoinLineupScRsp, SyncLineupNotify, SyncLineupReason } from "../../data/proto/StarRail";
|
||||||
JoinLineupCsReq,
|
import Avatar from "../../db/Avatar";
|
||||||
JoinLineupScRsp,
|
|
||||||
SyncLineupNotify,
|
|
||||||
SyncLineupReason
|
|
||||||
} from "../../data/proto/StarRail";
|
|
||||||
import Packet from "../kcp/Packet";
|
import Packet from "../kcp/Packet";
|
||||||
import Session from "../kcp/Session";
|
import Session from "../kcp/Session";
|
||||||
|
|
||||||
// JoinLineupCsReq { baseAvatarId: 1002, slot: 1 }
|
// JoinLineupCsReq { baseAvatarId: 1002, slot: 1 }
|
||||||
export default async function handle(session: Session, packet: Packet) {
|
export default async function handle(session: Session, packet: Packet) {
|
||||||
const body = packet.body as JoinLineupCsReq;
|
const body = packet.body as JoinLineupCsReq;
|
||||||
session.send(JoinLineupScRsp, {retcode: 0});
|
session.send(JoinLineupScRsp, { retcode: 0 });
|
||||||
|
|
||||||
// Replace avatar in the player's lineup.
|
// Replace avatar in the player's lineup.
|
||||||
const slot = body.slot ?? 0;
|
const slot = body.slot ?? 0;
|
||||||
const index = body.index ?? 1;
|
session.player.db.lineup.lineups[session.player.db.lineup.curIndex].avatarList[slot] = body.baseAvatarId;
|
||||||
|
|
||||||
session.player.db.lineup.lineups[index].avatarList[slot] = body.baseAvatarId;
|
|
||||||
await session.player.save();
|
await session.player.save();
|
||||||
|
|
||||||
session.send(SyncLineupNotify, {
|
session.send(SyncLineupNotify, {
|
||||||
lineup: await session.player.getLineup(index),
|
lineup: await session.player.getLineup(),
|
||||||
reasonList: [SyncLineupReason.SYNC_REASON_NONE]
|
reasonList: [SyncLineupReason.SYNC_REASON_NONE]
|
||||||
} as SyncLineupNotify);
|
} as SyncLineupNotify);
|
||||||
}
|
}
|
@ -1,70 +0,0 @@
|
|||||||
import { GetCurSceneInfoScRsp, LeaveChallengeScRsp } from "../../data/proto/StarRail";
|
|
||||||
import Packet from "../kcp/Packet";
|
|
||||||
import Session from "../kcp/Session";
|
|
||||||
import MapEntryExcel from "../../util/excel/MapEntryExcel";
|
|
||||||
import MazePlaneExcel from "../../util/excel/MazePlaneExcel";
|
|
||||||
import { ActorEntity } from "../../game/entities/Actor";
|
|
||||||
|
|
||||||
export default async function handle(session: Session, packet: Packet) {
|
|
||||||
const bfArray: number[] = [];
|
|
||||||
for (let i = 0; i < 500; i++) {
|
|
||||||
bfArray.push(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Challenge maze data doesn't get saved to DB.. We can abuse this and fall back to default
|
|
||||||
const posData = session.player.db.posData;
|
|
||||||
const entry = MapEntryExcel.fromFloorId(posData.floorID);
|
|
||||||
const maze = MazePlaneExcel.fromPlaneId(posData.planeID);
|
|
||||||
if (!entry || !maze) return session.send(LeaveChallengeScRsp, LeaveChallengeScRsp.fromPartial({ retcode: 2 }));
|
|
||||||
|
|
||||||
const dataObj = LeaveChallengeScRsp.fromPartial({
|
|
||||||
retcode: 0,
|
|
||||||
maze: {
|
|
||||||
floor: {
|
|
||||||
floorId: posData.floorID,
|
|
||||||
scene: {
|
|
||||||
entityList: [],
|
|
||||||
entryId: entry.ID,
|
|
||||||
floorId: entry.FloorID,
|
|
||||||
planeId: entry.PlaneID,
|
|
||||||
gameModeType: MazePlaneExcel.getGameModeForPlaneType(maze.PlaneType),
|
|
||||||
lightenSectionList: bfArray,
|
|
||||||
entityBuffList: [],
|
|
||||||
envBuffList: [],
|
|
||||||
}
|
|
||||||
},
|
|
||||||
id: posData.planeID,
|
|
||||||
mapEntryId: entry.ID
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
session.send(LeaveChallengeScRsp, dataObj);
|
|
||||||
} catch (e) {
|
|
||||||
session.c.error(e as Error);
|
|
||||||
session.send(LeaveChallengeScRsp, LeaveChallengeScRsp.fromPartial({ retcode: 2 }));
|
|
||||||
} finally {
|
|
||||||
// Force us back
|
|
||||||
const posData = session.player.db.posData;
|
|
||||||
const entry = MapEntryExcel.fromFloorId(posData.floorID);
|
|
||||||
const maze = MazePlaneExcel.fromPlaneId(posData.planeID);
|
|
||||||
if (entry && maze) {
|
|
||||||
const actor = new ActorEntity(session.player.scene, session.player.db.lineup.lineups[session.player.db.lineup.curIndex].avatarList[0], posData.pos).getSceneActorInfo();
|
|
||||||
|
|
||||||
session.send(GetCurSceneInfoScRsp, {
|
|
||||||
retcode: 0,
|
|
||||||
scene: {
|
|
||||||
planeId: posData.planeID,
|
|
||||||
floorId: posData.floorID,
|
|
||||||
lightenSectionList: bfArray,
|
|
||||||
gameModeType: MazePlaneExcel.getGameModeForPlaneType(maze.PlaneType),
|
|
||||||
entityBuffList: [],
|
|
||||||
envBuffList: [],
|
|
||||||
entryId: entry.ID,
|
|
||||||
entityList: [actor],
|
|
||||||
leaderEntityId: actor.entityId
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,26 +0,0 @@
|
|||||||
import { LockEquipmentCsReq, LockEquipmentScRsp } from "../../data/proto/StarRail";
|
|
||||||
import Packet from "../kcp/Packet";
|
|
||||||
import Session from "../kcp/Session";
|
|
||||||
|
|
||||||
export default async function handle(session: Session, packet: Packet) {
|
|
||||||
const body = packet.body as LockEquipmentCsReq;
|
|
||||||
|
|
||||||
// Fetch the equipment in question.
|
|
||||||
const inventory = await session.player.getInventory();
|
|
||||||
const equipment = inventory.getEquipmentByUid(body.equipmentUniqueId);
|
|
||||||
|
|
||||||
if (!equipment) {
|
|
||||||
session.send(LockEquipmentScRsp, { retcode: 1 } as LockEquipmentScRsp);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Toggle the equipment's lock.
|
|
||||||
equipment.isProtected = !equipment.isProtected;
|
|
||||||
await inventory.save();
|
|
||||||
|
|
||||||
// Done. Send and sync.
|
|
||||||
inventory.sendEquipmentUpdate();
|
|
||||||
session.send(LockEquipmentScRsp, {
|
|
||||||
retcode: 0,
|
|
||||||
equipmentUniqueId: body.equipmentUniqueId
|
|
||||||
} as LockEquipmentScRsp);
|
|
||||||
}
|
|
@ -1,34 +0,0 @@
|
|||||||
import { BattleEndStatus, EnterMazeCsReq, EnterMazeScRsp, Item, ItemList, PVEBattleResultCsReq, PVEBattleResultScRsp } from "../../data/proto/StarRail";
|
|
||||||
import MapEntryExcel from "../../util/excel/MapEntryExcel";
|
|
||||||
import MazePlaneExcel from "../../util/excel/MazePlaneExcel";
|
|
||||||
import Packet from "../kcp/Packet";
|
|
||||||
import Session from "../kcp/Session";
|
|
||||||
|
|
||||||
export default async function handle(session: Session, packet: Packet) {
|
|
||||||
const body = packet.body as PVEBattleResultCsReq;
|
|
||||||
|
|
||||||
// Add drops, for our little gambling addicts.
|
|
||||||
const inventory = await session.player.getInventory();
|
|
||||||
inventory.addItem(102, 10);
|
|
||||||
await inventory.save();
|
|
||||||
|
|
||||||
// Build response.
|
|
||||||
const dataObj : PVEBattleResultScRsp = {
|
|
||||||
retcode: 0,
|
|
||||||
stageId: body.stageId,
|
|
||||||
curFinishChallenge: 0,
|
|
||||||
dropData: { itemList: [{ itemId: 102, num: 10 } as Item] } as ItemList,
|
|
||||||
extraDropData: { itemList: [{ itemId: 102, num: 10 } as Item] } as ItemList,
|
|
||||||
avatarExpReward: 0,
|
|
||||||
binVer: "",
|
|
||||||
resVer: "",
|
|
||||||
battleId: body.battleId,
|
|
||||||
endStatus: body.endStatus,
|
|
||||||
checkIdentical: true,
|
|
||||||
eventId: 0,
|
|
||||||
mismatchTurnCount: 0
|
|
||||||
};
|
|
||||||
|
|
||||||
// Send response.
|
|
||||||
session.send(PVEBattleResultScRsp, dataObj);
|
|
||||||
}
|
|
@ -18,47 +18,33 @@ const retWarn = (msg: string) => c.warn(msg);
|
|||||||
|
|
||||||
export default async function handle(session: Session, packet: Packet) {
|
export default async function handle(session: Session, packet: Packet) {
|
||||||
const body = packet.body as PlayerGetTokenCsReq;
|
const body = packet.body as PlayerGetTokenCsReq;
|
||||||
|
|
||||||
const dataObj = {
|
const dataObj = {
|
||||||
retcode: 0,
|
retcode: 0,
|
||||||
secretKeySeed: 0,
|
secretKeySeed: 0,
|
||||||
} as PlayerGetTokenScRsp;
|
} as PlayerGetTokenScRsp;
|
||||||
|
|
||||||
try {
|
const account = await Account.fromToken(body.token || "");
|
||||||
const account = await Account.fromToken(body.token || "");
|
if (!account) retWarn(`Account not found with token ${body.token}`);
|
||||||
if (!account) retWarn(`Account not found with token ${body.token}`);
|
|
||||||
|
|
||||||
const player = await Player.fromToken(session, account?.token || "");
|
const player = await Player.fromToken(session, account?.token || "");
|
||||||
if (!player) retWarn(`Player not found with accountToken ${account?.token}`);
|
if (!player) retWarn(`Player not found with accountToken ${account?.token}`);
|
||||||
if (!player || !account) {
|
if (!player || !account) {
|
||||||
dataObj.retcode = 6;
|
dataObj.retcode = 6;
|
||||||
dataObj.msg = "Player not found";
|
return;
|
||||||
return;
|
}
|
||||||
}
|
session.account = account;
|
||||||
|
session.player = player;
|
||||||
|
|
||||||
session.account = account;
|
const isTokenValid = player.db.token === body.token;
|
||||||
session.player = player;
|
const isBanned = player.db.banned;
|
||||||
dataObj.uid = player.db._id;
|
if (isBanned) dataObj.retcode = 1013;
|
||||||
|
if (!isTokenValid) {
|
||||||
if (player.db.banned) {
|
retWarn(`Token invalid (${session.ctx.address}:${session.ctx.port})`);
|
||||||
dataObj.retcode = 1013;
|
dataObj.retcode = 1005;
|
||||||
dataObj.blackInfo = {
|
return;
|
||||||
banType: 2,
|
|
||||||
beginTime: Math.floor(Date.now() / 1000),
|
|
||||||
endTime: Math.floor(Date.now() / 1000) + 86400,
|
|
||||||
limitLevel: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (player.db.token !== body.token) {
|
|
||||||
retWarn(`Token invalid (${session.ctx.address}:${session.ctx.port})`);
|
|
||||||
dataObj.retcode = 1005;
|
|
||||||
dataObj.msg = "Token invalid";
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
dataObj.retcode = 2;
|
|
||||||
c.error(e as Error);
|
|
||||||
} finally {
|
|
||||||
session.send(PlayerGetTokenScRsp, dataObj);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dataObj.uid = player.db._id;
|
||||||
|
session.send(PlayerGetTokenScRsp, dataObj);
|
||||||
}
|
}
|
@ -1,7 +1,10 @@
|
|||||||
import { PromoteAvatarCsReq, PromoteAvatarScRsp } from "../../data/proto/StarRail";
|
import { PromoteAvatarCsReq, PromoteAvatarScRsp } from "../../data/proto/StarRail";
|
||||||
import Avatar from "../../db/Avatar";
|
import Avatar from "../../db/Avatar";
|
||||||
import { PayItemData } from "../../db/Inventory";
|
import { PayItemData } from "../../db/Inventory";
|
||||||
|
import AvatarExcel from "../../util/excel/AvatarExcel";
|
||||||
|
import AvatarExpItemExcel from "../../util/excel/AvatarExpItemExcel";
|
||||||
import AvatarPromotionExcel from "../../util/excel/AvatarPromotionExcel";
|
import AvatarPromotionExcel from "../../util/excel/AvatarPromotionExcel";
|
||||||
|
import ExpTypeExcel from "../../util/excel/ExpTypeExcel";
|
||||||
import Packet from "../kcp/Packet";
|
import Packet from "../kcp/Packet";
|
||||||
import Session from "../kcp/Session";
|
import Session from "../kcp/Session";
|
||||||
|
|
||||||
|
@ -1,36 +0,0 @@
|
|||||||
import { PromoteEquipmentCsReq, PromoteEquipmentScRsp } from "../../data/proto/StarRail";
|
|
||||||
import { PayItemData } from "../../db/Inventory";
|
|
||||||
import EquipmentPromotionExcel from "../../util/excel/EquipmentPromotionExcel";
|
|
||||||
import Packet from "../kcp/Packet";
|
|
||||||
import Session from "../kcp/Session";
|
|
||||||
|
|
||||||
export default async function handle(session: Session, packet: Packet) {
|
|
||||||
const body = packet.body as PromoteEquipmentCsReq;
|
|
||||||
const inventory = await session.player.getInventory();
|
|
||||||
|
|
||||||
// Get the target avatar.
|
|
||||||
const equipmentId = body.equipmentUniqueId;
|
|
||||||
const equipment = inventory.getEquipmentByUid(equipmentId);
|
|
||||||
const promotionExcelData = EquipmentPromotionExcel.fromId(`${equipment.tid}:${equipment.promotion}`);
|
|
||||||
|
|
||||||
// Build list of consumed items. We take this from the excel, instead of the Req.
|
|
||||||
const costMaterialList = promotionExcelData.PromotionCostList.map(c => { return { id: c.ItemID, count: c.ItemNum } as PayItemData });
|
|
||||||
|
|
||||||
// Try consuming materials.
|
|
||||||
const success = await inventory.payItems(costMaterialList);
|
|
||||||
if (!success) {
|
|
||||||
// ToDo: Correct retcode.
|
|
||||||
session.send(PromoteEquipmentScRsp, { retcode: 1 } as PromoteEquipmentScRsp);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await inventory.save();
|
|
||||||
|
|
||||||
// Promote the avatar and save.
|
|
||||||
equipment.promotion++;
|
|
||||||
await inventory.save();
|
|
||||||
|
|
||||||
// Done. Sync and send response.
|
|
||||||
await session.sync();
|
|
||||||
session.send(PromoteEquipmentScRsp, { retcode: 0 } as PromoteEquipmentScRsp);
|
|
||||||
}
|
|
@ -1,32 +0,0 @@
|
|||||||
import { RankUpEquipmentCsReq, RankUpEquipmentScRsp } from "../../data/proto/StarRail";
|
|
||||||
import Packet from "../kcp/Packet";
|
|
||||||
import Session from "../kcp/Session";
|
|
||||||
|
|
||||||
export default async function handle(session: Session, packet: Packet) {
|
|
||||||
const body = packet.body as RankUpEquipmentCsReq;
|
|
||||||
const inventory = await session.player.getInventory();
|
|
||||||
|
|
||||||
// Get equipment.
|
|
||||||
const equipment = inventory.getEquipmentByUid(body.equipmentUniqueId);
|
|
||||||
|
|
||||||
// Check if all sacrificed equipments exist.
|
|
||||||
for (const id of body.equipmentIdList) {
|
|
||||||
if (!inventory.getEquipmentByUid(id)) {
|
|
||||||
session.send(RankUpEquipmentScRsp, { retcode: 1 } as RankUpEquipmentScRsp);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove sacrificed equipments.
|
|
||||||
for (const id of body.equipmentIdList) {
|
|
||||||
await inventory.removeEquipment(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Increase rank and save.
|
|
||||||
equipment.rank += body.equipmentIdList.length;
|
|
||||||
await inventory.save();
|
|
||||||
|
|
||||||
// Done. Send and sync.
|
|
||||||
await session.sync();
|
|
||||||
session.send(RankUpEquipmentScRsp, { retcode: 0 } as RankUpEquipmentScRsp);
|
|
||||||
}
|
|
@ -5,7 +5,6 @@ import Session from "../kcp/Session";
|
|||||||
|
|
||||||
export default async function handle(session: Session, packet: Packet) {
|
export default async function handle(session: Session, packet: Packet) {
|
||||||
const body = packet.body as SceneEntityMoveCsReq;
|
const body = packet.body as SceneEntityMoveCsReq;
|
||||||
|
|
||||||
if (session.player.scene.entryId !== body.entryId) {
|
if (session.player.scene.entryId !== body.entryId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1,76 +1,59 @@
|
|||||||
import { ChallengeStatus, CurChallenge, ExtraLineupType, Maze, SceneActorInfo, SceneEntityInfo, SceneInfo, SceneNpcInfo, SceneNpcMonsterInfo, ScenePropInfo, StartChallengeCsReq, StartChallengeScRsp } from "../../data/proto/StarRail";
|
import { ChallengeStatus, ExtraLineupType, StartChallengeCsReq, StartChallengeScRsp } from "../../data/proto/StarRail";
|
||||||
import { ActorEntity } from "../../game/entities/Actor";
|
|
||||||
import Packet from "../kcp/Packet";
|
import Packet from "../kcp/Packet";
|
||||||
import Session from "../kcp/Session";
|
import Session from "../kcp/Session";
|
||||||
|
|
||||||
// StartChallengeCsReq { challengeId: 101 }
|
// StartChallengeCsReq { challengeId: 101 }
|
||||||
export default async function handle(session: Session, packet: Packet) {
|
export default async function handle(session: Session, packet: Packet) {
|
||||||
const body = packet.body as StartChallengeCsReq;
|
const body = packet.body as StartChallengeCsReq;
|
||||||
console.log(JSON.stringify(body, undefined, 4));
|
|
||||||
|
|
||||||
// TODO: This packet is just a base
|
// TODO: This packet is just a base
|
||||||
const _lineup = session.player.db.lineup;
|
|
||||||
const lineup = _lineup.lineups[_lineup.curIndex];
|
|
||||||
const curAvatarEntity = new ActorEntity(session.player.scene, lineup.avatarList[lineup.leaderSlot], { x: 0, y: 0, z: 0 });
|
|
||||||
|
|
||||||
session.send(StartChallengeScRsp, {
|
session.send(StartChallengeScRsp, {
|
||||||
retcode: 0,
|
retcode: 0,
|
||||||
curChallenge: {
|
curChallenge: {
|
||||||
challengeId: body.challengeId,
|
challengeId: body.challengeId,
|
||||||
|
deadAvatarNum: 0,
|
||||||
|
extraLineupType: ExtraLineupType.LINEUP_CHALLENGE,
|
||||||
rounds: 1,
|
rounds: 1,
|
||||||
status: ChallengeStatus.CHALLENGE_DOING,
|
status: ChallengeStatus.CHALLENGE_DOING,
|
||||||
extraLineupType: ExtraLineupType.LINEUP_NONE,
|
killMonsterList: [{
|
||||||
killMonsterList: [],
|
monsterId: 8013010,
|
||||||
deadAvatarNum: 0,
|
killNum: 1,
|
||||||
} as CurChallenge,
|
}]
|
||||||
|
},
|
||||||
maze: {
|
maze: {
|
||||||
// ? Data from MappingInfoExcelTable
|
// ? Data from MappingInfoExcelTable
|
||||||
id: 30104,
|
id: 30101,
|
||||||
mapEntryId: 3000401,
|
mapEntryId: 10001,
|
||||||
floor: {
|
floor: {
|
||||||
floorId: 20121001,
|
floorId: 20121001,
|
||||||
scene: {
|
scene: {
|
||||||
planeId: 30104,
|
planeId: 20121,
|
||||||
entryId: 3000401,
|
entryId: 1000,
|
||||||
floorId: 30104001,
|
floorId: 20121001,
|
||||||
lightenSectionList: [],
|
gameModeType: 1,
|
||||||
gameModeType: 4,
|
entityList: [{
|
||||||
entityList: [
|
entityId: 10010101,
|
||||||
curAvatarEntity.getSceneEntityInfo(),
|
npcMonster: {
|
||||||
{
|
monsterId: 8013010,
|
||||||
entityId: 10000,
|
worldLevel: 1,
|
||||||
motion: {
|
},
|
||||||
pos: {
|
groupId: 11,
|
||||||
x: 74719,
|
motion: {
|
||||||
y: 2014,
|
pos: {
|
||||||
z: -94205,
|
x: 0,
|
||||||
},
|
y: 100,
|
||||||
rot: {
|
z: 0,
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
z: 0
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
groupId: 3,
|
rot: {
|
||||||
instId: 1,
|
x: 0,
|
||||||
actor: {} as SceneActorInfo,
|
y: 0,
|
||||||
npc: {} as SceneNpcInfo,
|
z: 0
|
||||||
prop: {} as ScenePropInfo,
|
}
|
||||||
npcMonster: {
|
},
|
||||||
monsterId: 1003020,
|
}]
|
||||||
isGenMonster: false,
|
}
|
||||||
eventId: 0,
|
|
||||||
isSetWorldLevel: false,
|
|
||||||
worldLevel: 6
|
|
||||||
} as SceneNpcMonsterInfo
|
|
||||||
} as SceneEntityInfo,
|
|
||||||
],
|
|
||||||
leaderEntityId: curAvatarEntity.entityId,
|
|
||||||
entityBuffList: [],
|
|
||||||
envBuffList: [],
|
|
||||||
snar: ""
|
|
||||||
} as SceneInfo
|
|
||||||
}
|
}
|
||||||
} as Maze
|
}
|
||||||
} as StartChallengeScRsp);
|
} as StartChallengeScRsp);
|
||||||
}
|
}
|
@ -1,19 +0,0 @@
|
|||||||
import Session from "../kcp/Session";
|
|
||||||
import Packet from "../kcp/Packet";
|
|
||||||
import {SwitchLineupIndexCsReq, SwitchLineupIndexScRsp} from "../../data/proto/StarRail";
|
|
||||||
|
|
||||||
// SwitchLineupIndexCsReq { index: 0 }
|
|
||||||
export default async function handle(session: Session, packet: Packet) {
|
|
||||||
const body = packet.body as SwitchLineupIndexCsReq
|
|
||||||
const index = body.index ?? 0
|
|
||||||
|
|
||||||
session.send(SwitchLineupIndexScRsp, {
|
|
||||||
retcode: 0,
|
|
||||||
index: index
|
|
||||||
})
|
|
||||||
|
|
||||||
session.player.db.lineup.curIndex = index
|
|
||||||
await session.player.save()
|
|
||||||
|
|
||||||
// Todo: figure need to send SyncLineupNotify again ?
|
|
||||||
}
|
|
@ -1,54 +0,0 @@
|
|||||||
// @ts-ignore
|
|
||||||
import { RCONServer as RServer } from "rcon-server";
|
|
||||||
import { Writable } from "stream";
|
|
||||||
import Interface, { Command } from "../../commands/Interface";
|
|
||||||
import Config from "../../util/Config";
|
|
||||||
import Logger from "../../util/Logger";
|
|
||||||
const c = new Logger("RCON", "green");
|
|
||||||
|
|
||||||
export default class RCONServer {
|
|
||||||
private static _instance: RCONServer;
|
|
||||||
public RCON!: RServer;
|
|
||||||
|
|
||||||
private constructor() { }
|
|
||||||
|
|
||||||
public static getInstance(): RCONServer {
|
|
||||||
if (!this._instance) this._instance = new RCONServer();
|
|
||||||
return this._instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
public start() {
|
|
||||||
if (!Config.RCON.RCON_ENABLED) return;
|
|
||||||
this.RCON = new RServer({
|
|
||||||
host: Config.HTTP.HTTP_HOST,
|
|
||||||
clientLimit: Config.RCON.RCON_CLIENT_LIMIT,
|
|
||||||
destroySocketOnLimitExceeded: false,
|
|
||||||
emitAdvancedEvents: false,
|
|
||||||
password: Config.RCON.RCON_PASSWORD,
|
|
||||||
port: Config.RCON.RCON_PORT
|
|
||||||
});
|
|
||||||
|
|
||||||
this.RCON.on("listening", () => {
|
|
||||||
c.log(`Listening on ${Config.RCON.RCON_PORT}`);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.RCON.on("commandRequest", (cmd: {
|
|
||||||
size: number,
|
|
||||||
id: number,
|
|
||||||
type: number,
|
|
||||||
body: string
|
|
||||||
resolve: (value: string) => void,
|
|
||||||
}) => {
|
|
||||||
c.verbL(cmd);
|
|
||||||
Interface.handle(new Command(cmd.body));
|
|
||||||
cmd.resolve(`Command executed.`);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.RCON.on("login", ({ pw, fail }: { pw: string, fail: boolean }) => {
|
|
||||||
c.debug(pw);
|
|
||||||
fail ? c.log(`Login failed`) : c.log(`Login successful`); // For some reason succ is flipped
|
|
||||||
});
|
|
||||||
|
|
||||||
this.RCON.connect();
|
|
||||||
}
|
|
||||||
}
|
|
263
src/server/rcon/rcon-server.d.ts
vendored
263
src/server/rcon/rcon-server.d.ts
vendored
@ -1,263 +0,0 @@
|
|||||||
/** Declaration file generated by dts-gen */
|
|
||||||
import { EventEmitter } from "events";
|
|
||||||
|
|
||||||
declare module "rcon-server" {
|
|
||||||
export class RCONServer extends EventEmitter {
|
|
||||||
constructor(obj: {
|
|
||||||
port: number = 3839,
|
|
||||||
host: string = "127.0.0.1",
|
|
||||||
password: string = "password",
|
|
||||||
clientLimit: number = 1,
|
|
||||||
destroySocketOnLimitExceeded: boolean = true,
|
|
||||||
emitAdvancedEvents: boolean = false
|
|
||||||
});
|
|
||||||
|
|
||||||
connect(...args: any[]): void;
|
|
||||||
|
|
||||||
getConnectedSockets(...args: any[]): void;
|
|
||||||
|
|
||||||
getServerSettings(...args: any[]): void;
|
|
||||||
|
|
||||||
getSocketServer(...args: any[]): void;
|
|
||||||
|
|
||||||
static captureRejectionSymbol: any;
|
|
||||||
|
|
||||||
static captureRejections: boolean;
|
|
||||||
|
|
||||||
static defaultMaxListeners: number;
|
|
||||||
|
|
||||||
static errorMonitor: any;
|
|
||||||
|
|
||||||
static getEventListeners(emitterOrTarget: any, type: any): any;
|
|
||||||
|
|
||||||
static init(opts: any): void;
|
|
||||||
|
|
||||||
static kMaxEventTargetListeners: any;
|
|
||||||
|
|
||||||
static kMaxEventTargetListenersWarned: any;
|
|
||||||
|
|
||||||
static listenerCount(emitter: any, type: any): any;
|
|
||||||
|
|
||||||
static on(emitter: any, event: any, options: any): any;
|
|
||||||
|
|
||||||
static once(emitter: any, name: any, options: any): any;
|
|
||||||
|
|
||||||
static setMaxListeners(n: any, eventTargets: any): void;
|
|
||||||
|
|
||||||
static usingDomains: boolean;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
export namespace RCONServer {
|
|
||||||
class EventEmitter {
|
|
||||||
constructor(opts: any);
|
|
||||||
|
|
||||||
addListener(type: any, listener: any): any;
|
|
||||||
|
|
||||||
emit(type: any, args: any): any;
|
|
||||||
|
|
||||||
eventNames(): any;
|
|
||||||
|
|
||||||
getMaxListeners(): any;
|
|
||||||
|
|
||||||
listenerCount(type: any): any;
|
|
||||||
|
|
||||||
listeners(type: any): any;
|
|
||||||
|
|
||||||
off(type: any, listener: any): any;
|
|
||||||
|
|
||||||
on(type: any, listener: any): any;
|
|
||||||
|
|
||||||
once(type: any, listener: any): any;
|
|
||||||
|
|
||||||
prependListener(type: any, listener: any): any;
|
|
||||||
|
|
||||||
prependOnceListener(type: any, listener: any): any;
|
|
||||||
|
|
||||||
rawListeners(type: any): any;
|
|
||||||
|
|
||||||
removeAllListeners(type: any, ...args: any[]): any;
|
|
||||||
|
|
||||||
removeListener(type: any, listener: any): any;
|
|
||||||
|
|
||||||
setMaxListeners(n: any): any;
|
|
||||||
|
|
||||||
static EventEmitter: any;
|
|
||||||
|
|
||||||
static captureRejectionSymbol: any;
|
|
||||||
|
|
||||||
static captureRejections: boolean;
|
|
||||||
|
|
||||||
static defaultMaxListeners: number;
|
|
||||||
|
|
||||||
static errorMonitor: any;
|
|
||||||
|
|
||||||
static getEventListeners(emitterOrTarget: any, type: any): any;
|
|
||||||
|
|
||||||
static init(opts: any): void;
|
|
||||||
|
|
||||||
static kMaxEventTargetListeners: any;
|
|
||||||
|
|
||||||
static kMaxEventTargetListenersWarned: any;
|
|
||||||
|
|
||||||
static listenerCount(emitter: any, type: any): any;
|
|
||||||
|
|
||||||
static on(emitter: any, event: any, options: any): any;
|
|
||||||
|
|
||||||
static once(emitter: any, name: any, options: any): any;
|
|
||||||
|
|
||||||
static setMaxListeners(n: any, eventTargets: any): void;
|
|
||||||
|
|
||||||
static usingDomains: boolean;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
class EventEmitterAsyncResource {
|
|
||||||
constructor(...args: any[]);
|
|
||||||
|
|
||||||
emit(...args: any[]): void;
|
|
||||||
|
|
||||||
emitDestroy(...args: any[]): void;
|
|
||||||
|
|
||||||
static EventEmitterAsyncResource: any;
|
|
||||||
|
|
||||||
static captureRejectionSymbol: any;
|
|
||||||
|
|
||||||
static captureRejections: boolean;
|
|
||||||
|
|
||||||
static defaultMaxListeners: number;
|
|
||||||
|
|
||||||
static errorMonitor: any;
|
|
||||||
|
|
||||||
static getEventListeners(emitterOrTarget: any, type: any): any;
|
|
||||||
|
|
||||||
static init(opts: any): void;
|
|
||||||
|
|
||||||
static kMaxEventTargetListeners: any;
|
|
||||||
|
|
||||||
static kMaxEventTargetListenersWarned: any;
|
|
||||||
|
|
||||||
static listenerCount(emitter: any, type: any): any;
|
|
||||||
|
|
||||||
static on(emitter: any, event: any, options: any): any;
|
|
||||||
|
|
||||||
static once(emitter: any, name: any, options: any): any;
|
|
||||||
|
|
||||||
static setMaxListeners(n: any, eventTargets: any): void;
|
|
||||||
|
|
||||||
static usingDomains: boolean;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace EventEmitter {
|
|
||||||
class EventEmitterAsyncResource {
|
|
||||||
constructor(...args: any[]);
|
|
||||||
|
|
||||||
emit(...args: any[]): void;
|
|
||||||
|
|
||||||
emitDestroy(...args: any[]): void;
|
|
||||||
|
|
||||||
static EventEmitter: any;
|
|
||||||
|
|
||||||
static EventEmitterAsyncResource: any;
|
|
||||||
|
|
||||||
static captureRejectionSymbol: any;
|
|
||||||
|
|
||||||
static captureRejections: boolean;
|
|
||||||
|
|
||||||
static defaultMaxListeners: number;
|
|
||||||
|
|
||||||
static errorMonitor: any;
|
|
||||||
|
|
||||||
static getEventListeners(emitterOrTarget: any, type: any): any;
|
|
||||||
|
|
||||||
static init(opts: any): void;
|
|
||||||
|
|
||||||
static kMaxEventTargetListeners: any;
|
|
||||||
|
|
||||||
static kMaxEventTargetListenersWarned: any;
|
|
||||||
|
|
||||||
static listenerCount(emitter: any, type: any): any;
|
|
||||||
|
|
||||||
static on(emitter: any, event: any, options: any): any;
|
|
||||||
|
|
||||||
static once(emitter: any, name: any, options: any): any;
|
|
||||||
|
|
||||||
static setMaxListeners(n: any, eventTargets: any): void;
|
|
||||||
|
|
||||||
static usingDomains: boolean;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace EventEmitterAsyncResource {
|
|
||||||
class EventEmitter {
|
|
||||||
constructor(opts: any);
|
|
||||||
|
|
||||||
addListener(type: any, listener: any): any;
|
|
||||||
|
|
||||||
emit(type: any, args: any): any;
|
|
||||||
|
|
||||||
eventNames(): any;
|
|
||||||
|
|
||||||
getMaxListeners(): any;
|
|
||||||
|
|
||||||
listenerCount(type: any): any;
|
|
||||||
|
|
||||||
listeners(type: any): any;
|
|
||||||
|
|
||||||
off(type: any, listener: any): any;
|
|
||||||
|
|
||||||
on(type: any, listener: any): any;
|
|
||||||
|
|
||||||
once(type: any, listener: any): any;
|
|
||||||
|
|
||||||
prependListener(type: any, listener: any): any;
|
|
||||||
|
|
||||||
prependOnceListener(type: any, listener: any): any;
|
|
||||||
|
|
||||||
rawListeners(type: any): any;
|
|
||||||
|
|
||||||
removeAllListeners(type: any, ...args: any[]): any;
|
|
||||||
|
|
||||||
removeListener(type: any, listener: any): any;
|
|
||||||
|
|
||||||
setMaxListeners(n: any): any;
|
|
||||||
|
|
||||||
static EventEmitter: any;
|
|
||||||
|
|
||||||
static EventEmitterAsyncResource: any;
|
|
||||||
|
|
||||||
static captureRejectionSymbol: any;
|
|
||||||
|
|
||||||
static captureRejections: boolean;
|
|
||||||
|
|
||||||
static defaultMaxListeners: number;
|
|
||||||
|
|
||||||
static errorMonitor: any;
|
|
||||||
|
|
||||||
static getEventListeners(emitterOrTarget: any, type: any): any;
|
|
||||||
|
|
||||||
static init(opts: any): void;
|
|
||||||
|
|
||||||
static kMaxEventTargetListeners: any;
|
|
||||||
|
|
||||||
static kMaxEventTargetListenersWarned: any;
|
|
||||||
|
|
||||||
static listenerCount(emitter: any, type: any): any;
|
|
||||||
|
|
||||||
static on(emitter: any, event: any, options: any): any;
|
|
||||||
|
|
||||||
static once(emitter: any, name: any, options: any): any;
|
|
||||||
|
|
||||||
static setMaxListeners(n: any, eventTargets: any): void;
|
|
||||||
|
|
||||||
static usingDomains: boolean;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -28,12 +28,6 @@ const DEFAULT_CONFIG = {
|
|||||||
MAINTENANCE: false,
|
MAINTENANCE: false,
|
||||||
MAINTENANCE_MSG: "Server is in maintenance mode."
|
MAINTENANCE_MSG: "Server is in maintenance mode."
|
||||||
},
|
},
|
||||||
RCON: {
|
|
||||||
RCON_ENABLED: false,
|
|
||||||
RCON_PASSWORD: "password",
|
|
||||||
RCON_PORT: 22103,
|
|
||||||
RCON_CLIENT_LIMIT: 1
|
|
||||||
},
|
|
||||||
AUTO_ACCOUNT: false
|
AUTO_ACCOUNT: false
|
||||||
}
|
}
|
||||||
type DefaultConfig = typeof DEFAULT_CONFIG;
|
type DefaultConfig = typeof DEFAULT_CONFIG;
|
||||||
@ -88,12 +82,6 @@ export default class Config {
|
|||||||
MAINTENANCE_MSG: string;
|
MAINTENANCE_MSG: string;
|
||||||
} = Config.config.GAMESERVER;
|
} = Config.config.GAMESERVER;
|
||||||
public static AUTO_ACCOUNT: boolean = Config.config.AUTO_ACCOUNT;
|
public static AUTO_ACCOUNT: boolean = Config.config.AUTO_ACCOUNT;
|
||||||
public static RCON: {
|
|
||||||
RCON_ENABLED: boolean;
|
|
||||||
RCON_PASSWORD: string;
|
|
||||||
RCON_PORT: number;
|
|
||||||
RCON_CLIENT_LIMIT: number;
|
|
||||||
} = Config.config.RCON;
|
|
||||||
|
|
||||||
private constructor() { }
|
private constructor() { }
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ const c = new Logger("ProtoFactory");
|
|||||||
export class MessageType<T> {
|
export class MessageType<T> {
|
||||||
"encode": (arg0: T) => protobufjs.Writer;
|
"encode": (arg0: T) => protobufjs.Writer;
|
||||||
"fromPartial": (arg0: object) => T;
|
"fromPartial": (arg0: object) => T;
|
||||||
"decode": (input: protobufjs.Reader | Uint8Array, length?: number)=> T;
|
// "decode": (input: protobufjs.Reader | Uint8Array, length?: number)=> T;
|
||||||
// "fromJSON": (object: any)=>T;
|
// "fromJSON": (object: any)=>T;
|
||||||
// "toJSON": (message: T)=> unknown
|
// "toJSON": (message: T)=> unknown
|
||||||
//you can add more fields here from the generated types
|
//you can add more fields here from the generated types
|
||||||
|
@ -1,20 +0,0 @@
|
|||||||
import _EquipmentExcelTable from "../../data/excel/EquipmentExcelTable.json";
|
|
||||||
type EquipmentExcelTableEntry = typeof _EquipmentExcelTable[keyof typeof _EquipmentExcelTable]
|
|
||||||
const EquipmentExcelTable = _EquipmentExcelTable as { [key: string]: EquipmentExcelTableEntry };
|
|
||||||
|
|
||||||
export default class EquipmentExcel {
|
|
||||||
private constructor() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public static all() : EquipmentExcelTableEntry[] {
|
|
||||||
return Object.values(EquipmentExcelTable);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static fromId(id: number) : EquipmentExcelTableEntry {
|
|
||||||
return EquipmentExcelTable[id];
|
|
||||||
}
|
|
||||||
|
|
||||||
public static fromIds(ids: number[]): EquipmentExcelTableEntry[] {
|
|
||||||
return ids.map(id => EquipmentExcel.fromId(id));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,20 +0,0 @@
|
|||||||
import _EquipmentExpItemExcelTable from "../../data/excel/EquipmentExpItemExcelTable.json";
|
|
||||||
type EquipmentExpItemExcelTableEntry = typeof _EquipmentExpItemExcelTable[keyof typeof _EquipmentExpItemExcelTable]
|
|
||||||
const EquipmentExpItemExcelTable = _EquipmentExpItemExcelTable as { [key: string]: EquipmentExpItemExcelTableEntry };
|
|
||||||
|
|
||||||
export default class EquipmentExpItemExcel {
|
|
||||||
private constructor() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public static all() : EquipmentExpItemExcelTableEntry[] {
|
|
||||||
return Object.values(EquipmentExpItemExcelTable);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static fromId(id: number) : EquipmentExpItemExcelTableEntry {
|
|
||||||
return EquipmentExpItemExcelTable[id];
|
|
||||||
}
|
|
||||||
|
|
||||||
public static fromIds(ids: number[]): EquipmentExpItemExcelTableEntry[] {
|
|
||||||
return ids.map(id => EquipmentExpItemExcel.fromId(id));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,20 +0,0 @@
|
|||||||
import _EquipmentExpTypeExcelTable from "../../data/excel/EquipmentExpTypeExcelTable.json";
|
|
||||||
type EquipmentExpTypeExcelTableEntry = typeof _EquipmentExpTypeExcelTable[keyof typeof _EquipmentExpTypeExcelTable]
|
|
||||||
const EquipmentExpTypeExcelTable = _EquipmentExpTypeExcelTable as { [key: string]: EquipmentExpTypeExcelTableEntry };
|
|
||||||
|
|
||||||
export default class EquipmentExpTypeExcel {
|
|
||||||
private constructor() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public static all() : EquipmentExpTypeExcelTableEntry[] {
|
|
||||||
return Object.values(EquipmentExpTypeExcelTable);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static fromId(id: string) : EquipmentExpTypeExcelTableEntry {
|
|
||||||
return EquipmentExpTypeExcelTable[id];
|
|
||||||
}
|
|
||||||
|
|
||||||
public static fromIds(ids: string[]): EquipmentExpTypeExcelTableEntry[] {
|
|
||||||
return ids.map(id => EquipmentExpTypeExcel.fromId(id));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,20 +0,0 @@
|
|||||||
import _EquipmentPromotionExcelTable from "../../data/excel/EquipmentPromotionExcelTable.json";
|
|
||||||
type EquipmentPromotionExcelTableEntry = typeof _EquipmentPromotionExcelTable[keyof typeof _EquipmentPromotionExcelTable]
|
|
||||||
const EquipmentPromotionExcelTable = _EquipmentPromotionExcelTable as { [key: string]: EquipmentPromotionExcelTableEntry };
|
|
||||||
|
|
||||||
export default class EquipmentPromotionExcel {
|
|
||||||
private constructor() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public static all() : EquipmentPromotionExcelTableEntry[] {
|
|
||||||
return Object.values(EquipmentPromotionExcelTable);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static fromId(id: string) : EquipmentPromotionExcelTableEntry {
|
|
||||||
return EquipmentPromotionExcelTable[id];
|
|
||||||
}
|
|
||||||
|
|
||||||
public static fromIds(ids: string[]): EquipmentPromotionExcelTableEntry[] {
|
|
||||||
return ids.map(id => EquipmentPromotionExcel.fromId(id));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,24 +0,0 @@
|
|||||||
import _MapEntryExcelTable from "../../data/excel/MapEntryExcelTable.json";
|
|
||||||
type MapEntryExcelTableEntry = typeof _MapEntryExcelTable[keyof typeof _MapEntryExcelTable]
|
|
||||||
const MapEntryExcelTable = _MapEntryExcelTable as { [key: string]: MapEntryExcelTableEntry };
|
|
||||||
|
|
||||||
export default class MapEntryExcel {
|
|
||||||
private constructor() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public static all() : MapEntryExcelTableEntry[] {
|
|
||||||
return Object.values(MapEntryExcelTable);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static fromId(id: number) : MapEntryExcelTableEntry {
|
|
||||||
return MapEntryExcelTable[id];
|
|
||||||
}
|
|
||||||
|
|
||||||
public static fromIds(ids: number[]): MapEntryExcelTableEntry[] {
|
|
||||||
return ids.map(id => MapEntryExcel.fromId(id));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static fromFloorId(id: number) : MapEntryExcelTableEntry {
|
|
||||||
return Object.values(MapEntryExcelTable).filter(e => e.FloorID == id)?.[0];
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,5 +1,5 @@
|
|||||||
|
import _MapEntryExcelTable from "../../data/excel/MapEntryExcelTable.json";
|
||||||
import _MazePlaneExcelTable from "../../data/excel/MazePlaneExcelTable.json";
|
import _MazePlaneExcelTable from "../../data/excel/MazePlaneExcelTable.json";
|
||||||
import MapEntryExcel from "./MapEntryExcel";
|
|
||||||
|
|
||||||
interface MazePlaneExcelTableEntry {
|
interface MazePlaneExcelTableEntry {
|
||||||
PlaneID: number;
|
PlaneID: number;
|
||||||
@ -11,13 +11,44 @@ interface MazePlaneExcelTableEntry {
|
|||||||
FloorIDList: number[];
|
FloorIDList: number[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface TextMap {
|
||||||
|
hash: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
type EntranceType = "Town" | "Mission" | "Explore";
|
||||||
|
|
||||||
|
interface MapEntryExcelTableEntry {
|
||||||
|
ID: number;
|
||||||
|
IsShowInMapMenu: boolean;
|
||||||
|
MapMenuSortID: number;
|
||||||
|
EntranceType: EntranceType | number; // Actually an enum. Town | Mission | Explore
|
||||||
|
EntranceGroupID: number;
|
||||||
|
Name: TextMap;
|
||||||
|
Desc: TextMap;
|
||||||
|
EntranceListIcon: string;
|
||||||
|
ImagePath: string;
|
||||||
|
MiniMapIconHintList: any[];
|
||||||
|
ShowReward: number;
|
||||||
|
PlaneID: number;
|
||||||
|
FloorID: number;
|
||||||
|
StartGroupID: number;
|
||||||
|
StartAnchorID: number;
|
||||||
|
TargetMission: number;
|
||||||
|
TargetMainMissionList: number[];
|
||||||
|
BeginMainMissionList: number[];
|
||||||
|
FinishMainMissionList: number[];
|
||||||
|
FinishQuestList: number[];
|
||||||
|
UnlockQuest: number;
|
||||||
|
}
|
||||||
|
|
||||||
const MazePlaneExcelTable = _MazePlaneExcelTable as { [key: string]: MazePlaneExcelTableEntry };
|
const MazePlaneExcelTable = _MazePlaneExcelTable as { [key: string]: MazePlaneExcelTableEntry };
|
||||||
|
const MapEntryExcelTable = _MapEntryExcelTable as { [key: string]: MapEntryExcelTableEntry };
|
||||||
|
|
||||||
export default class MazePlaneExcel {
|
export default class MazePlaneExcel {
|
||||||
private constructor() { }
|
private constructor() { }
|
||||||
|
|
||||||
public static fromEntryId(entryId: number): MazePlaneExcelTableEntry {
|
public static fromEntryId(entryId: number): MazePlaneExcelTableEntry {
|
||||||
const mapEntry = MapEntryExcel.fromId(entryId);
|
const mapEntry = MapEntryExcelTable[entryId.toString()];
|
||||||
return MazePlaneExcelTable[mapEntry.PlaneID.toString()];
|
return MazePlaneExcelTable[mapEntry.PlaneID.toString()];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -25,21 +56,11 @@ export default class MazePlaneExcel {
|
|||||||
return MazePlaneExcelTable[planeId.toString()];
|
return MazePlaneExcelTable[planeId.toString()];
|
||||||
}
|
}
|
||||||
|
|
||||||
public static getGameModeForPlaneType(planeType: string): number {
|
public static getEntry(entryId: number): MapEntryExcelTableEntry {
|
||||||
switch (planeType) {
|
return MapEntryExcelTable[entryId.toString()];
|
||||||
case "Town": return 1;
|
}
|
||||||
case "Maze": return 2;
|
|
||||||
case "Train": return 3;
|
|
||||||
case "Challenge": return 4;
|
|
||||||
case "RogueExplore": return 5;
|
|
||||||
case "RogueChallenge": return 6;
|
|
||||||
case "TownRoom": return 7;
|
|
||||||
case "Raid": return 8;
|
|
||||||
case "FarmRelic": return 9;
|
|
||||||
case "Client": return 10;
|
|
||||||
case "ChallengeActivity": return 11;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
public static getAllEntries(): MapEntryExcelTableEntry[] {
|
||||||
|
return Object.values(MapEntryExcelTable);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,20 +0,0 @@
|
|||||||
import _MonsterExcelTable from "../../data/excel/MonsterExcelTable.json";
|
|
||||||
type MonsterExcelTableEntry = typeof _MonsterExcelTable[keyof typeof _MonsterExcelTable]
|
|
||||||
const MonsterExcelTable = _MonsterExcelTable as { [key: string]: MonsterExcelTableEntry };
|
|
||||||
|
|
||||||
export default class MonsterExcel {
|
|
||||||
private constructor() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public static all() : MonsterExcelTableEntry[] {
|
|
||||||
return Object.values(MonsterExcelTable);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static fromId(id: number) : MonsterExcelTableEntry {
|
|
||||||
return MonsterExcelTable[id];
|
|
||||||
}
|
|
||||||
|
|
||||||
public static fromIds(ids: number[]): MonsterExcelTableEntry[] {
|
|
||||||
return ids.map(id => MonsterExcel.fromId(id));
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user