Compare commits
1 Commits
main
...
daily-miss
Author | SHA1 | Date | |
---|---|---|---|
|
f7d827e991 |
1
.gitignore
vendored
1
.gitignore
vendored
@ -110,4 +110,3 @@ dist
|
|||||||
# CrepeSR
|
# CrepeSR
|
||||||
config.json
|
config.json
|
||||||
src/data/*
|
src/data/*
|
||||||
banners.json
|
|
@ -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,28 +23,17 @@ 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);
|
|
||||||
Interface.start();
|
|
||||||
});
|
|
||||||
|
|
||||||
Interface.rl.on('close', () => {
|
|
||||||
console.log('Have a great day!');
|
|
||||||
process.exit(0);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public static handle(cmd: Command) {
|
|
||||||
import(`./${alias[cmd.name] || cmd.name}`).then(async module => {
|
import(`./${alias[cmd.name] || cmd.name}`).then(async module => {
|
||||||
await module.default(cmd);
|
await module.default(cmd);
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
@ -54,5 +43,12 @@ export default class Interface {
|
|||||||
}
|
}
|
||||||
c.error(err);
|
c.error(err);
|
||||||
});
|
});
|
||||||
|
Interface.start();
|
||||||
|
});
|
||||||
|
|
||||||
|
Interface.rl.on('close', () => {
|
||||||
|
console.log('Have a great day!');
|
||||||
|
process.exit(0);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -13,7 +13,6 @@ export default async function handle(command: Command) {
|
|||||||
const actionType = command.args[0];
|
const actionType = command.args[0];
|
||||||
const avatarId = Number(command.args[1]);
|
const avatarId = Number(command.args[1]);
|
||||||
const uid = Interface.target.player.db._id;
|
const uid = Interface.target.player.db._id;
|
||||||
const player = Interface.target.player;
|
|
||||||
|
|
||||||
switch (actionType) {
|
switch (actionType) {
|
||||||
default: {
|
default: {
|
||||||
@ -21,32 +20,21 @@ export default async function handle(command: Command) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "add": {
|
case "add": {
|
||||||
if (!avatarId) {
|
if (!avatarId) return c.log("No avatarId specified");
|
||||||
return c.log("No avatarId specified");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if it already exists
|
// Check if it already exists
|
||||||
if (await Avatar.hasAvatar(player, avatarId)) {
|
const avatar = await Avatar.fromUID(uid, avatarId);
|
||||||
return c.log(`Avatar ${avatarId} already exists`);
|
if (avatar.length > 0) return c.log(`Avatar ${avatarId} already exists`);
|
||||||
}
|
Avatar.create(uid, avatarId).then(a => c.log(`Avatar ${avatarId} added to ${a.ownerUid}`));
|
||||||
|
|
||||||
await Avatar.addAvatarToPlayer(player, avatarId).then(a => c.log(`Avatar ${avatarId} added to ${a.db.ownerUid}`));
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "remove": {
|
case "remove": {
|
||||||
if (!avatarId) return c.log("No avatarId specified");
|
if (!avatarId) return c.log("No avatarId specified");
|
||||||
await Avatar.removeAvatarFromPlayer(player, avatarId).then(() => c.log(`Avatar ${avatarId} removed from ${uid}`));
|
Avatar.remove(uid, avatarId).then(() => c.log(`Avatar ${avatarId} removed from ${uid}`));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "giveall": {
|
case "giveall": {
|
||||||
for (const id in AvatarExcelTable) {
|
for (const id in AvatarExcelTable) {
|
||||||
const avatarId = Number(id);
|
await Avatar.create(uid, parseInt(id));
|
||||||
// Let's not brick our account.
|
|
||||||
if (avatarId>= 8000) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
await Avatar.addAvatarToPlayer(player, avatarId);
|
|
||||||
}
|
}
|
||||||
c.log(`All avatars added to ${uid}`);
|
c.log(`All avatars added to ${uid}`);
|
||||||
break;
|
break;
|
||||||
@ -54,7 +42,7 @@ export default async function handle(command: Command) {
|
|||||||
case "removeall": {
|
case "removeall": {
|
||||||
for (const id in AvatarExcelTable) {
|
for (const id in AvatarExcelTable) {
|
||||||
if (Number(id) !== 1001) {
|
if (Number(id) !== 1001) {
|
||||||
await Avatar.removeAvatarFromPlayer(player, parseInt(id));
|
await Avatar.remove(uid, parseInt(id));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
c.log(`All avatars removed from ${uid}`);
|
c.log(`All avatars removed from ${uid}`);
|
||||||
|
@ -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);
|
|
||||||
}
|
|
@ -8,8 +8,8 @@ export default async function handle(command: Command) {
|
|||||||
else {
|
else {
|
||||||
let level = parseInt(command.args[0]);
|
let level = parseInt(command.args[0]);
|
||||||
if (!level) level = 0;
|
if (!level) level = 0;
|
||||||
if (level > VerboseLevel.VERBH || level < 0) {
|
if (level > 2 || level < 0) {
|
||||||
c.log("Invalid verbose level. Must be between 0 and 4.");
|
c.log("Invalid verbose level. Must be between 0 and 2.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,110 +0,0 @@
|
|||||||
import { Equipment } from "../data/proto/StarRail";
|
|
||||||
import Player from "../db/Player";
|
|
||||||
import ItemExcel from "../util/excel/ItemExcel";
|
|
||||||
import Logger from "../util/Logger";
|
|
||||||
import Interface, { Command } from "./Interface";
|
|
||||||
const c = new Logger("/item", "blue");
|
|
||||||
|
|
||||||
export default async function handle(command: Command) {
|
|
||||||
if (!Interface.target) {
|
|
||||||
c.log("No target specified");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const player = Interface.target.player;
|
|
||||||
const actionType = command.args[0];
|
|
||||||
const itemId = Number(command.args[1]);
|
|
||||||
|
|
||||||
let count: number = 1;
|
|
||||||
let level: number = 1;
|
|
||||||
let rank: number = 0;
|
|
||||||
let promotion: number = 0;
|
|
||||||
|
|
||||||
for (let i = 2; i < command.args.length; i++) {
|
|
||||||
const arg = command.args[i];
|
|
||||||
const number = Number(command.args[i].substring(1));
|
|
||||||
|
|
||||||
if (arg.startsWith("x")) {
|
|
||||||
count = number;
|
|
||||||
}
|
|
||||||
else if (arg.startsWith("l")) {
|
|
||||||
level = number;
|
|
||||||
}
|
|
||||||
else if (arg.startsWith("r")) {
|
|
||||||
rank = number;
|
|
||||||
}
|
|
||||||
else if (arg.startsWith("p")) {
|
|
||||||
promotion = number;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (actionType) {
|
|
||||||
case "give": {
|
|
||||||
await handleGive(player, itemId, count, level, rank, promotion);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "giveall": {
|
|
||||||
await handleGiveAll(player);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
c.log(`Usage: /item <give|giveall> <itemId> [x<count>|l<level>|r<rank>|p<promotion>]*`);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sync session.
|
|
||||||
await player.session.sync();
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleGive(player: Player, itemId: number, count:number, level: number, rank: number, promotion: number) {
|
|
||||||
if (!itemId) {
|
|
||||||
return c.log("No avatarId specified");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if this item exists.
|
|
||||||
const itemData = ItemExcel.fromId(itemId);
|
|
||||||
if (!itemData) {
|
|
||||||
return c.log(`Item ID ${itemId} does not exist.`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const inventory = await player.getInventory();
|
|
||||||
switch (itemData.ItemType) {
|
|
||||||
case "Material":
|
|
||||||
await inventory.addMaterial(itemId, count);
|
|
||||||
break;
|
|
||||||
case "Equipment":
|
|
||||||
for (let i = 0; i < count; i++) {
|
|
||||||
await inventory.addEquipment({
|
|
||||||
tid: itemId,
|
|
||||||
uniqueId: 0,
|
|
||||||
level: level,
|
|
||||||
rank: rank,
|
|
||||||
exp: 1,
|
|
||||||
isProtected: false,
|
|
||||||
promotion: promotion,
|
|
||||||
baseAvatarId: 0
|
|
||||||
} as Equipment);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return c.log(`Unsupported item type: ${itemData.ItemType}.`);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
c.log(`Added ${count} of item ${itemId} to player ${player.uid}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleGiveAll(player: Player) {
|
|
||||||
const inventory = await player.getInventory();
|
|
||||||
|
|
||||||
for (const entry of ItemExcel.all()) {
|
|
||||||
const count =
|
|
||||||
(entry.ItemType == "Material") ? 1000 :
|
|
||||||
(entry.ItemType == "Virtual") ? 10_000_000 :
|
|
||||||
1;
|
|
||||||
await inventory.addItem(entry.ID, count);
|
|
||||||
}
|
|
||||||
|
|
||||||
c.log(`All materials added to ${player.uid}`);
|
|
||||||
}
|
|
@ -1,3 +1,5 @@
|
|||||||
|
import { BlackLimitLevel, PlayerKickOutScNotify, PlayerKickOutScNotify_KickType } from "../data/proto/StarRail";
|
||||||
|
import SRServer from "../server/kcp/SRServer";
|
||||||
import Logger from "../util/Logger";
|
import Logger from "../util/Logger";
|
||||||
import Interface, { Command } from "./Interface";
|
import Interface, { Command } from "./Interface";
|
||||||
const c = new Logger("/kick", "blue");
|
const c = new Logger("/kick", "blue");
|
||||||
@ -8,7 +10,17 @@ export default async function handle(command: Command) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Interface.target.kick();
|
Interface.target.send("PlayerKickOutScNotify", {
|
||||||
|
kickType: PlayerKickOutScNotify_KickType.KICK_BLACK,
|
||||||
|
blackInfo: {
|
||||||
|
limitLevel: BlackLimitLevel.BLACK_LIMIT_LEVEL_ALL,
|
||||||
|
beginTime: Math.round(Date.now() / 1000),
|
||||||
|
endTime: Math.round(Date.now() / 1000),
|
||||||
|
banType: 2
|
||||||
|
}
|
||||||
|
} as PlayerKickOutScNotify);
|
||||||
|
|
||||||
|
// SRServer.getInstance().sessions.delete(`${Interface.target.ctx.address}:${Interface.target.ctx.port}`);
|
||||||
|
|
||||||
c.log(`Kicked ${Interface.target.account.name}`);
|
c.log(`Kicked ${Interface.target.account.name}`);
|
||||||
}
|
}
|
@ -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}.`);
|
|
||||||
}
|
|
@ -1,50 +0,0 @@
|
|||||||
import Logger from "../util/Logger";
|
|
||||||
import { ActorEntity } from "../game/entities/Actor";
|
|
||||||
import Interface, { Command } from "./Interface";
|
|
||||||
import { GetCurSceneInfoScRsp } from "../data/proto/StarRail";
|
|
||||||
import MazePlaneExcel from "../util/excel/MazePlaneExcel";
|
|
||||||
import MapEntryExcel from "../util/excel/MapEntryExcel";
|
|
||||||
const c = new Logger("/scene", "blue");
|
|
||||||
|
|
||||||
export default async function handle(command: Command) {
|
|
||||||
if (!Interface.target) {
|
|
||||||
c.log("No target specified");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const planeID = MazePlaneExcel.fromPlaneId(parseInt(command.args[0]));
|
|
||||||
const entryId = MapEntryExcel.fromFloorId(planeID.StartFloorID).ID;
|
|
||||||
const posData = Interface.target.player.db.posData;
|
|
||||||
|
|
||||||
const lineup2 = await Interface.target.player.getLineup();
|
|
||||||
const curAvatarEntity = new ActorEntity(Interface.target.player.scene, lineup2.avatarList[0].id, posData.pos);
|
|
||||||
|
|
||||||
if (!planeID) return c.log("Usage: /scene <planeID>");
|
|
||||||
|
|
||||||
// Update scene information on player.
|
|
||||||
Interface.target.player.db.posData.planeID = planeID!.PlaneID;
|
|
||||||
Interface.target.player.db.posData.floorID = planeID!.StartFloorID;
|
|
||||||
await Interface.target.player.save()
|
|
||||||
|
|
||||||
//ty for tamilpp25 scene
|
|
||||||
Interface.target.send(GetCurSceneInfoScRsp, {
|
|
||||||
retcode: 0,
|
|
||||||
scene: {
|
|
||||||
planeId: planeID.PlaneID,
|
|
||||||
floorId: planeID.StartFloorID,
|
|
||||||
entityList: [
|
|
||||||
curAvatarEntity
|
|
||||||
],
|
|
||||||
entityBuffList: [],
|
|
||||||
entryId: entryId,
|
|
||||||
envBuffList: [],
|
|
||||||
gameModeType: MazePlaneExcel.getGameModeForPlaneType(planeID.PlaneType),
|
|
||||||
lightenSectionList: []
|
|
||||||
},
|
|
||||||
} as unknown as GetCurSceneInfoScRsp);
|
|
||||||
Interface.target.player.scene.spawnEntity(curAvatarEntity, true);
|
|
||||||
|
|
||||||
Interface.target.sync();
|
|
||||||
|
|
||||||
c.log(`Scene set to PlaneID: ${planeID.PlaneID}`);
|
|
||||||
}
|
|
@ -1,14 +0,0 @@
|
|||||||
import Logger from "../util/Logger";
|
|
||||||
import Interface, { Command } from "./Interface";
|
|
||||||
const c = new Logger("/sync", "blue");
|
|
||||||
|
|
||||||
export default async function handle(command: Command) {
|
|
||||||
if (!Interface.target) {
|
|
||||||
c.log("No target specified");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Interface.target.sync();
|
|
||||||
|
|
||||||
c.log(`Synced ${Interface.target.account.name}`);
|
|
||||||
}
|
|
@ -15,27 +15,22 @@ 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: Number(client.player.db._id),
|
||||||
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]) {
|
|
||||||
c.log(`Auto targetting the only session ${possibleTargets[0].uid}`);
|
|
||||||
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}`);
|
||||||
}
|
}
|
177
src/db/Avatar.ts
177
src/db/Avatar.ts
@ -1,167 +1,68 @@
|
|||||||
// import { Avatar as AvatarI, AvatarType, LineupAvatar } from '../data/proto/StarRail';
|
import { Avatar as AvatarI, AvatarType, LineupAvatar } from '../data/proto/StarRail';
|
||||||
import { AvatarSkillTree, AvatarType, EquipRelic, Avatar as AvatarProto } from '../data/proto/StarRail';
|
|
||||||
import Logger from '../util/Logger';
|
import Logger from '../util/Logger';
|
||||||
import Database from './Database';
|
import Database from './Database';
|
||||||
import Player, { LineupI } from './Player';
|
import Player, { LineupI } from './Player';
|
||||||
const c = new Logger("Avatar");
|
const c = new Logger("Avatar");
|
||||||
type UID = number | string;
|
type UID = number | string;
|
||||||
|
|
||||||
interface AvatarI {
|
|
||||||
ownerUid: number,
|
|
||||||
baseAvatarId: number,
|
|
||||||
avatarType: AvatarType,
|
|
||||||
level: number,
|
|
||||||
exp: number,
|
|
||||||
promotion: number,
|
|
||||||
rank: number,
|
|
||||||
equipmentUniqueId: number,
|
|
||||||
equipRelicList: EquipRelic[],
|
|
||||||
skilltreeList: AvatarSkillTree[],
|
|
||||||
fightProps: {
|
|
||||||
hp: number,
|
|
||||||
sp: number,
|
|
||||||
satiety: number
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class Avatar {
|
export default class Avatar {
|
||||||
public readonly player: Player;
|
private constructor(public ownerUid: UID, public data: AvatarI, public lineup: LineupAvatar) {
|
||||||
public readonly db: AvatarI;
|
|
||||||
|
|
||||||
private constructor(player: Player, db: AvatarI) {
|
|
||||||
this.player = player;
|
|
||||||
this.db = db;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/********************************************************************************
|
public static async create(uid: UID, baseAvatarId: number = 1001, slot: number = -1): Promise<Avatar> {
|
||||||
Create and fetch avatars from the database.
|
|
||||||
********************************************************************************/
|
|
||||||
|
|
||||||
public static async loadAvatarsForPlayer(player: Player) : Promise<Avatar[]> {
|
|
||||||
// Read avatars for this player from the database.
|
|
||||||
const db = Database.getInstance();
|
const db = Database.getInstance();
|
||||||
const avatars = await db.getAll("avatars", { ownerUid: player.uid }) as unknown as AvatarI[];
|
// Check if already exists
|
||||||
|
const existing = await Avatar.fromUID(uid, baseAvatarId);
|
||||||
// If this player doesn't have any avatars yet, add a default.
|
if (existing.length > 0) return existing[0];
|
||||||
if (avatars.length < 1) {
|
const avatar = new Avatar(uid, {
|
||||||
avatars.push({
|
baseAvatarId,
|
||||||
ownerUid: player.uid,
|
equipmentUniqueId: 20003, // TODO: Placeholder while we work on inventory system
|
||||||
baseAvatarId: 1001,
|
|
||||||
avatarType: AvatarType.AVATAR_FORMAL_TYPE,
|
|
||||||
level: 1,
|
|
||||||
exp: 0,
|
|
||||||
promotion: 0,
|
|
||||||
rank: 0,
|
|
||||||
equipmentUniqueId: 20003,
|
|
||||||
equipRelicList: [],
|
equipRelicList: [],
|
||||||
skilltreeList: [],
|
|
||||||
fightProps: {
|
|
||||||
hp: 10000,
|
|
||||||
sp: 10000,
|
|
||||||
satiety: 100
|
|
||||||
}
|
|
||||||
} as AvatarI);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Construct Avatar instances.
|
|
||||||
const res: Avatar[] = []
|
|
||||||
for (const avatar of avatars) {
|
|
||||||
res.push(new Avatar(player, avatar));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Done.
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async loadAvatarForPlayer(player: Player, baseAvatarId: number) : Promise<Avatar> {
|
|
||||||
// Fetch the given avatar from the database.
|
|
||||||
const db = Database.getInstance();
|
|
||||||
const avatar = await db.get("avatars", { ownerUid: player.uid, baseAvatarId: baseAvatarId }) as unknown as AvatarI;
|
|
||||||
|
|
||||||
// Sanity check.
|
|
||||||
if (!avatar) {
|
|
||||||
throw new Error(`Avatar ${baseAvatarId} does not exist for player ${player.uid}. This should never happen. Check your logic at the callsite.`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Done.
|
|
||||||
return new Avatar(player, avatar);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async hasAvatar(player: Player, baseAvatarId: number) : Promise<boolean> {
|
|
||||||
// Fetch the given avatar from the database.
|
|
||||||
const db = Database.getInstance();
|
|
||||||
const avatar = await db.get("avatars", { ownerUid: player.uid, baseAvatarId: baseAvatarId }) as unknown as AvatarI;
|
|
||||||
|
|
||||||
// Return.
|
|
||||||
return avatar ? true : false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async addAvatarToPlayer(player: Player, baseAvatarId: number) : Promise<Avatar> {
|
|
||||||
const db = Database.getInstance();
|
|
||||||
|
|
||||||
// Make sure the player doesn't already have that avatar.
|
|
||||||
const existingAvatar = await db.get("avatars", { ownerUid: player.uid, baseAvatarId: baseAvatarId }) as unknown as AvatarI;
|
|
||||||
if (existingAvatar) {
|
|
||||||
return new Avatar(player, existingAvatar);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Insert.
|
|
||||||
const data : AvatarI = {
|
|
||||||
ownerUid: player.uid,
|
|
||||||
baseAvatarId: baseAvatarId,
|
|
||||||
avatarType: AvatarType.AVATAR_FORMAL_TYPE,
|
|
||||||
level: 1,
|
|
||||||
exp: 0,
|
exp: 0,
|
||||||
promotion: 0,
|
level: 1,
|
||||||
rank: 0,
|
promotion: 1,
|
||||||
equipmentUniqueId: 20003,
|
rank: 1,
|
||||||
equipRelicList: [],
|
|
||||||
skilltreeList: [],
|
skilltreeList: [],
|
||||||
fightProps: {
|
}, {
|
||||||
|
avatarType: AvatarType.AVATAR_FORMAL_TYPE,
|
||||||
hp: 10000,
|
hp: 10000,
|
||||||
sp: 10000,
|
id: baseAvatarId,
|
||||||
satiety: 100
|
satiety: 100,
|
||||||
}
|
slot: slot,
|
||||||
};
|
sp: 10000
|
||||||
await db.set("avatars", data);
|
});
|
||||||
return new Avatar(player, data);
|
db.set("avatars", avatar);
|
||||||
|
return avatar;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async removeAvatarFromPlayer(player: Player, baseAvatarId: number) {
|
public static async fromUID(ownerUid: UID, baseAvatarId?: number): Promise<Avatar[]> {
|
||||||
|
const query = { ownerUid } as { ownerUid: UID, "data.baseAvatarId"?: number };
|
||||||
|
if (baseAvatarId) query['data.baseAvatarId'] = baseAvatarId;
|
||||||
const db = Database.getInstance();
|
const db = Database.getInstance();
|
||||||
await db.delete("avatars", { ownerUid: player.uid, baseAvatarId: baseAvatarId });
|
return await db.getAll("avatars", query) as unknown as Avatar[];
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async getAvatarsForLineup(player: Player, lineup: LineupI) : Promise<Avatar[]> {
|
public static async fromLineup(uid: UID, lineup: LineupI): Promise<Avatar[]> {
|
||||||
const res: Avatar[] = [];
|
try {
|
||||||
|
const avatarList: Array<Avatar> = [];
|
||||||
|
|
||||||
// Load all avatars in this lineup.
|
for (let i = 0; i < lineup.avatarList.length; i++) {
|
||||||
for (const avatarId of lineup.avatarList) {
|
const avatarId = lineup.avatarList[i];
|
||||||
res.push(await Avatar.loadAvatarForPlayer(player, avatarId));
|
const avatar = await Avatar.fromUID(uid, avatarId);
|
||||||
|
avatarList.push(avatar[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Done.
|
return await Promise.all(avatarList);
|
||||||
return res;
|
} catch (e) {
|
||||||
|
c.error(e as Error);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async save() {
|
public static async remove(ownerUid: UID, baseAvatarId: number): Promise<void> {
|
||||||
const db = Database.getInstance();
|
const db = Database.getInstance();
|
||||||
await db.update("avatars", { ownerUid: this.player.uid, baseAvatarId: this.db.baseAvatarId }, this.db);
|
await db.delete("avatars", { ownerUid, "data.baseAvatarId": baseAvatarId });
|
||||||
}
|
}
|
||||||
|
|
||||||
/********************************************************************************
|
|
||||||
Get avatar info.
|
|
||||||
********************************************************************************/
|
|
||||||
public asAvatarProto() : AvatarProto {
|
|
||||||
return {
|
|
||||||
baseAvatarId: this.db.baseAvatarId,
|
|
||||||
exp: this.db.exp,
|
|
||||||
level: this.db.level,
|
|
||||||
promotion: this.db.promotion,
|
|
||||||
rank: this.db.rank,
|
|
||||||
skilltreeList: this.db.skilltreeList,
|
|
||||||
equipmentUniqueId: this.db.equipmentUniqueId,
|
|
||||||
equipRelicList: this.db.equipRelicList
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -28,7 +28,7 @@ export default class Database {
|
|||||||
const _collection = db.collection(collection);
|
const _collection = db.collection(collection);
|
||||||
if (!(await db.listCollections({ name: collection }).toArray()).length) {
|
if (!(await db.listCollections({ name: collection }).toArray()).length) {
|
||||||
c.warn(`Collection ${collection} does not exist. Creating...`);
|
c.warn(`Collection ${collection} does not exist. Creating...`);
|
||||||
await _collection.createIndexes([{ key: { _id: 1 } }]);
|
await _collection.createIndexes([{ key: { id: 1 }, unique: true }]);
|
||||||
}
|
}
|
||||||
const result = query ? await _collection.findOne(query) : await _collection.findOne();
|
const result = query ? await _collection.findOne(query) : await _collection.findOne();
|
||||||
return result;
|
return result;
|
||||||
@ -44,7 +44,7 @@ export default class Database {
|
|||||||
const _collection = db.collection(collection);
|
const _collection = db.collection(collection);
|
||||||
if (!(await db.listCollections({ name: collection }).toArray()).length) {
|
if (!(await db.listCollections({ name: collection }).toArray()).length) {
|
||||||
c.warn(`Collection ${collection} does not exist. Creating...`);
|
c.warn(`Collection ${collection} does not exist. Creating...`);
|
||||||
await _collection.createIndexes([{ key: { _id: 1 } }]);
|
await _collection.createIndexes([{ key: { id: 1 }, unique: true }]);
|
||||||
}
|
}
|
||||||
const result = query ? await _collection.find(query).toArray() : await _collection.find().toArray();
|
const result = query ? await _collection.find(query).toArray() : await _collection.find().toArray();
|
||||||
return result;
|
return result;
|
||||||
@ -60,7 +60,7 @@ export default class Database {
|
|||||||
const _collection = db.collection(collection);
|
const _collection = db.collection(collection);
|
||||||
if (!(await db.listCollections({ name: collection }).toArray()).length) {
|
if (!(await db.listCollections({ name: collection }).toArray()).length) {
|
||||||
c.warn(`Collection ${collection} does not exist. Creating...`);
|
c.warn(`Collection ${collection} does not exist. Creating...`);
|
||||||
await _collection.createIndexes([{ key: { _id: 1 } }]);
|
await _collection.createIndexes([{ key: { id: 1 }, unique: true }]);
|
||||||
}
|
}
|
||||||
return await _collection.insertOne(payload);
|
return await _collection.insertOne(payload);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@ -74,7 +74,7 @@ export default class Database {
|
|||||||
const _collection = db.collection(collection);
|
const _collection = db.collection(collection);
|
||||||
if (!(await db.listCollections({ name: collection }).toArray()).length) {
|
if (!(await db.listCollections({ name: collection }).toArray()).length) {
|
||||||
c.warn(`Collection ${collection} does not exist. Creating...`);
|
c.warn(`Collection ${collection} does not exist. Creating...`);
|
||||||
await _collection.createIndexes([{ key: { _id: 1 } }]);
|
await _collection.createIndexes([{ key: { id: 1 }, unique: true }]);
|
||||||
}
|
}
|
||||||
return await _collection.deleteOne(query);
|
return await _collection.deleteOne(query);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@ -88,7 +88,7 @@ export default class Database {
|
|||||||
const _collection = db.collection(collection);
|
const _collection = db.collection(collection);
|
||||||
if (!(await db.listCollections({ name: collection }).toArray()).length) {
|
if (!(await db.listCollections({ name: collection }).toArray()).length) {
|
||||||
c.warn(`Collection ${collection} does not exist. Creating...`);
|
c.warn(`Collection ${collection} does not exist. Creating...`);
|
||||||
await _collection.createIndexes([{ key: { _id: 1 } }]);
|
await _collection.createIndexes([{ key: { id: 1 }, unique: true }]);
|
||||||
}
|
}
|
||||||
return await _collection.updateOne(query, { $set: payload }, { upsert: true });
|
return await _collection.updateOne(query, { $set: payload }, { upsert: true });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -1,414 +0,0 @@
|
|||||||
import { Equipment, Item, Material, PlayerSyncScNotify, Relic } from "../data/proto/StarRail";
|
|
||||||
import Logger from "../util/Logger";
|
|
||||||
import Database from "./Database";
|
|
||||||
import Player from "./Player";
|
|
||||||
import ItemExcel from "../util/excel/ItemExcel";
|
|
||||||
|
|
||||||
const c = new Logger("Inventory");
|
|
||||||
|
|
||||||
export interface PayItemData {
|
|
||||||
id: number,
|
|
||||||
count: number
|
|
||||||
}
|
|
||||||
|
|
||||||
interface InventoryI {
|
|
||||||
_id: number,
|
|
||||||
nextItemUid: number,
|
|
||||||
materials: { [key: number]: number },
|
|
||||||
relics: Relic[],
|
|
||||||
equipments: Equipment[]
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class Inventory {
|
|
||||||
public readonly player : Player;
|
|
||||||
public readonly db: InventoryI;
|
|
||||||
|
|
||||||
private constructor(player: Player, db: InventoryI) {
|
|
||||||
this.player = player;
|
|
||||||
this.db = db;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async loadOrCreate(player: Player) : Promise<Inventory> {
|
|
||||||
// Try to load the player's inventory from the database.
|
|
||||||
const db = Database.getInstance();
|
|
||||||
const inventory = await db.get("inventory", { _id: player.uid }) as unknown as InventoryI; // How to get rid of this ugly fuck?!
|
|
||||||
|
|
||||||
// If successfull, we are done.
|
|
||||||
if (inventory) {
|
|
||||||
return new Inventory(player, inventory);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise, we create a default inventory.
|
|
||||||
const data : InventoryI = {
|
|
||||||
_id: player.uid,
|
|
||||||
nextItemUid: 1,
|
|
||||||
materials: {},
|
|
||||||
relics: [],
|
|
||||||
equipments: []
|
|
||||||
};
|
|
||||||
await db.set("inventory", data);
|
|
||||||
return new Inventory(player, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async save() {
|
|
||||||
const db = Database.getInstance();
|
|
||||||
await db.update("inventory", { _id: this.db._id }, this.db);
|
|
||||||
}
|
|
||||||
|
|
||||||
/********************************************************************************
|
|
||||||
Get inventory info.
|
|
||||||
********************************************************************************/
|
|
||||||
/**
|
|
||||||
* Get list of all `Material`s as proto.
|
|
||||||
* @returns List of materials.
|
|
||||||
*/
|
|
||||||
public getMaterialList() : Material[] {
|
|
||||||
const res: Material[] = [];
|
|
||||||
|
|
||||||
Object.keys(this.db.materials).forEach(key => {
|
|
||||||
res.push({ tid: Number(key), num: this.db.materials[Number(key)] } as Material);
|
|
||||||
});
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get list of all `Equipment`s as proto.
|
|
||||||
* @returns List of equipments.
|
|
||||||
*/
|
|
||||||
public getEquipmentList() : Equipment[] {
|
|
||||||
return this.db.equipments;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get list of all `Relic`s as proto.
|
|
||||||
* @returns List of relics.
|
|
||||||
*/
|
|
||||||
public getRelicsList() : Relic[] {
|
|
||||||
return this.db.relics;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the count of the given item (material or virtual) in the player's inventory.
|
|
||||||
* @param id The item id.
|
|
||||||
* @returns The count in the player's inventory.
|
|
||||||
*/
|
|
||||||
public async getItemCount(id: number) : Promise<number> {
|
|
||||||
// Get item data.
|
|
||||||
const itemData = ItemExcel.fromId(id);
|
|
||||||
if (!itemData) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (itemData.ItemType) {
|
|
||||||
case "Virtual": return this.getVirtualItemCount(id);
|
|
||||||
case "Material": return this.db.materials[id] ?? 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private getVirtualItemCount(id: number) : number {
|
|
||||||
// ToDo: Figure out which virtual item ID is what.
|
|
||||||
switch (id) {
|
|
||||||
case 2:
|
|
||||||
return this.player.db.basicInfo.scoin;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 the given amount of the given item to the player's inventory.
|
|
||||||
* @param id The item id. For equipment and relics, this is the base id.
|
|
||||||
* @param count The amount of items to add.
|
|
||||||
*/
|
|
||||||
public async addItem(id: number, count: number) {
|
|
||||||
// Get info for the particular item we are trying to add.
|
|
||||||
const itemData = ItemExcel.fromId(id);
|
|
||||||
if (!itemData) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle adding depending on item type.
|
|
||||||
const t = itemData.ItemType;
|
|
||||||
if (t == "Virtual") {
|
|
||||||
await this.addVirtualItem(id, count);
|
|
||||||
}
|
|
||||||
else if (t == "Material") {
|
|
||||||
await this.addMaterial(id, count);
|
|
||||||
}
|
|
||||||
else if (t == "Equipment") {
|
|
||||||
for (let i = 0; i < count; i++) {
|
|
||||||
await this.addEquipment(id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (t == "Relic") {
|
|
||||||
for (let i = 0; i < count; i++) {
|
|
||||||
await this.addRelic(id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds the given amount of the virtual item with the given id to the player's inventory.
|
|
||||||
* @param id The item id.
|
|
||||||
* @param count The amount.
|
|
||||||
*/
|
|
||||||
public async addVirtualItem(id: number, count: number) {
|
|
||||||
// ToDo: Figure out which virtual item ID is what.
|
|
||||||
switch (id) {
|
|
||||||
case 2:
|
|
||||||
this.player.db.basicInfo.scoin += count;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save.
|
|
||||||
this.player.save();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds the given amount of the material with the given id to the player's inventory.
|
|
||||||
* @param id The material id.
|
|
||||||
* @param count The amount.
|
|
||||||
*/
|
|
||||||
public async addMaterial(id: number, count: number) {
|
|
||||||
// Get info for the particular item we are trying to add.
|
|
||||||
const itemData = ItemExcel.fromId(id);
|
|
||||||
if (!itemData || itemData.ItemType != "Material") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get current item count for this ID and calculate new count.
|
|
||||||
const currentCount = this.db.materials[id] ?? 0;
|
|
||||||
const newCount = Math.min(currentCount + count, itemData.PileLimit);
|
|
||||||
|
|
||||||
// Update.
|
|
||||||
this.db.materials[id] = newCount;
|
|
||||||
await this.save();
|
|
||||||
|
|
||||||
// Send update.
|
|
||||||
this.sendMaterialUpdate();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds the given equipment to the player's inventory.
|
|
||||||
* @param equipment Either an `Equipment`, or the base id.
|
|
||||||
*/
|
|
||||||
public async addEquipment(equipment: number | Equipment) {
|
|
||||||
// If the parameter is a number, add a new equipment with this item ID as base.
|
|
||||||
if (typeof(equipment) == "number") {
|
|
||||||
// Sanity check.
|
|
||||||
if (ItemExcel.fromId(equipment)?.ItemType != "Equipment") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const equip : Equipment = {
|
|
||||||
tid: equipment,
|
|
||||||
uniqueId: this.db.nextItemUid++,
|
|
||||||
level: 1,
|
|
||||||
rank: 1,
|
|
||||||
exp: 1,
|
|
||||||
isProtected: false,
|
|
||||||
promotion: 1,
|
|
||||||
baseAvatarId: 0
|
|
||||||
};
|
|
||||||
|
|
||||||
this.db.equipments.push(equip);
|
|
||||||
await this.save();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise, add the equipment object directly, but reset it's UID.
|
|
||||||
equipment.uniqueId = this.db.nextItemUid++;
|
|
||||||
this.db.equipments.push(equipment);
|
|
||||||
await this.save();
|
|
||||||
|
|
||||||
// Send update.
|
|
||||||
this.sendEquipmentUpdate();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds the given relic to the player's inventory.
|
|
||||||
* @param relic Either a `Relic`, or the base id.
|
|
||||||
*/
|
|
||||||
public async addRelic(relic: number | Relic) {
|
|
||||||
// Don't add relics for now until we figure out affix IDs, since the game kinda breaks with
|
|
||||||
// incorrect ones.
|
|
||||||
return;
|
|
||||||
|
|
||||||
// If the parameter is a number, add a new equipment with this item ID as base.
|
|
||||||
/*if (typeof(relic) == "number") {
|
|
||||||
const rel : Relic = {
|
|
||||||
tid: relic,
|
|
||||||
uniqueId: this.db.nextItemUid++,
|
|
||||||
level: 1,
|
|
||||||
exp: 1,
|
|
||||||
isProtected: false,
|
|
||||||
baseAvatarId: 0,
|
|
||||||
mainAffixId: 1,
|
|
||||||
subAffixList: []
|
|
||||||
};
|
|
||||||
|
|
||||||
this.db.relics.push(rel);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise, add the equipment object directly, but reset it's UID.
|
|
||||||
relic.uniqueId = this.db.nextItemUid++;
|
|
||||||
this.db.relics.push(relic);*/
|
|
||||||
}
|
|
||||||
|
|
||||||
/********************************************************************************
|
|
||||||
Remove items from the inventory directly.
|
|
||||||
********************************************************************************/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes the the given number of the given item (virtual or material) with the given ID.
|
|
||||||
* @param id The item ID.
|
|
||||||
* @param count The number to remove.
|
|
||||||
*/
|
|
||||||
public async removeItem(id: number, count: number) {
|
|
||||||
const itemData = ItemExcel.fromId(id);
|
|
||||||
if (!itemData) {
|
|
||||||
return ;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (itemData.ItemType) {
|
|
||||||
case "Virtual": await this.removeVirtualItem(id, count); break;
|
|
||||||
case "Material": await this.removeMaterial(id, count); break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes the given amount of the given virtual item from the player's inventory.
|
|
||||||
* @param id The item id.
|
|
||||||
* @param count The amount.
|
|
||||||
*/
|
|
||||||
public async removeVirtualItem(id: number, count: number) {
|
|
||||||
await this.addVirtualItem(id, -count);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes the given amount of the given material from the player's inventory.
|
|
||||||
* @param id The item id.
|
|
||||||
* @param count The amount.
|
|
||||||
*/
|
|
||||||
public async removeMaterial(id: number, count: number) {
|
|
||||||
await this.addMaterial(id, -count);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes the given equipment player's inventory.
|
|
||||||
* @param equipment Either an `Equipment`, or the equipment's unique id.
|
|
||||||
*/
|
|
||||||
public async removeEquipment(equipment: number | Equipment) {
|
|
||||||
// Find index to delete.
|
|
||||||
const toDelete: number = (typeof(equipment) == "number") ? equipment : equipment.uniqueId;
|
|
||||||
const index = this.db.equipments.findIndex(i => i.uniqueId == toDelete);
|
|
||||||
if (index == -1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete and save.
|
|
||||||
this.db.equipments.splice(index, 1);
|
|
||||||
this.save();
|
|
||||||
|
|
||||||
// Send update.
|
|
||||||
this.sendEquipmentUpdate();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes the given relic player's inventory.
|
|
||||||
* @param relic Either a `Relic`, or the relic's unique id.
|
|
||||||
*/
|
|
||||||
public async removeRelic(relic: number | Relic) {
|
|
||||||
// Find index to delete.
|
|
||||||
const toDelete: number = (typeof(relic) == "number") ? relic : relic.uniqueId;
|
|
||||||
const index = this.db.relics.findIndex(i => i.uniqueId == toDelete);
|
|
||||||
if (index == -1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete and save.
|
|
||||||
this.db.relics.splice(index, 1);
|
|
||||||
this.save();
|
|
||||||
|
|
||||||
// Send update.
|
|
||||||
this.sendRelicUpdate()
|
|
||||||
}
|
|
||||||
|
|
||||||
/********************************************************************************
|
|
||||||
Pay items.
|
|
||||||
********************************************************************************/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pay items (virtual items and materials).
|
|
||||||
* @param items The items to be paid.
|
|
||||||
* @returns True if paying succeeded, false otherwise.
|
|
||||||
*/
|
|
||||||
public async payItems(items: PayItemData[]) : Promise<boolean> {
|
|
||||||
// Check if the player has a sufficient amount of all necessary items.
|
|
||||||
for (const item of items) {
|
|
||||||
const currentCount = await this.getItemCount(item.id);
|
|
||||||
if (currentCount < item.count) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// We have enough of everything - pay.
|
|
||||||
for (const item of items) {
|
|
||||||
await this.removeItem(item.id, item.count);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send update.
|
|
||||||
this.sendMaterialUpdate();
|
|
||||||
|
|
||||||
// Done.
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/********************************************************************************
|
|
||||||
Player updating.
|
|
||||||
********************************************************************************/
|
|
||||||
/**
|
|
||||||
* Send `PlayerSyncScNotify` for materials.
|
|
||||||
*/
|
|
||||||
public sendMaterialUpdate() {
|
|
||||||
this.player.session.send(PlayerSyncScNotify, PlayerSyncScNotify.fromPartial({
|
|
||||||
materialList: this.getMaterialList()
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send `PlayerSyncScNotify` for equipments.
|
|
||||||
*/
|
|
||||||
public sendEquipmentUpdate() {
|
|
||||||
this.player.session.send(PlayerSyncScNotify, PlayerSyncScNotify.fromPartial({
|
|
||||||
equipmentList: this.getEquipmentList()
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send `PlayerSyncScNotify` for relics.
|
|
||||||
*/
|
|
||||||
public sendRelicUpdate() {
|
|
||||||
this.player.session.send(PlayerSyncScNotify, PlayerSyncScNotify.fromPartial({
|
|
||||||
relicList: this.getRelicsList()
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,11 +1,10 @@
|
|||||||
import Session from "../server/kcp/Session";
|
import Session from "../server/kcp/Session";
|
||||||
import { AvatarType, ExtraLineupType, HeroBasicType, LineupAvatar, LineupInfo, Vector } from "../data/proto/StarRail";
|
import { AvatarType, ExtraLineupType, HeroBasicType, LineupInfo, Vector } from "../data/proto/StarRail";
|
||||||
import Logger from "../util/Logger";
|
import Logger from "../util/Logger";
|
||||||
import Account from "./Account";
|
import Account from "./Account";
|
||||||
import Avatar from "./Avatar";
|
import Avatar from "./Avatar";
|
||||||
import Database from "./Database";
|
import Database from "./Database";
|
||||||
import { Scene } from "../game/Scene";
|
import { Scene } from "../game/Scene";
|
||||||
import Inventory from "./Inventory";
|
|
||||||
const c = new Logger("Player");
|
const c = new Logger("Player");
|
||||||
|
|
||||||
export interface LineupI {
|
export interface LineupI {
|
||||||
@ -43,20 +42,15 @@ interface PlayerI {
|
|||||||
posData: {
|
posData: {
|
||||||
floorID: number;
|
floorID: number;
|
||||||
planeID: number;
|
planeID: number;
|
||||||
pos: {
|
pos: Vector;
|
||||||
x: number,
|
|
||||||
y: number,
|
|
||||||
z: number
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class Player {
|
export default class Player {
|
||||||
public readonly uid: number
|
public readonly uid: number
|
||||||
public readonly scene: Scene;
|
public readonly scene: Scene;
|
||||||
private inventory!: Inventory;
|
|
||||||
|
|
||||||
private constructor(readonly session: Session, public readonly db: PlayerI) {
|
private constructor(readonly session: Session, public db: PlayerI) {
|
||||||
this.uid = db._id;
|
this.uid = db._id;
|
||||||
this.scene = new Scene(this);
|
this.scene = new Scene(this);
|
||||||
}
|
}
|
||||||
@ -71,35 +65,32 @@ export default class Player {
|
|||||||
|
|
||||||
public static async fromToken(session: Session, token: string): Promise<Player | undefined> {
|
public static async fromToken(session: Session, token: string): Promise<Player | undefined> {
|
||||||
const db = Database.getInstance();
|
const db = Database.getInstance();
|
||||||
const plr = await db.get("players", { token: token }) as unknown as PlayerI;
|
const plr = await db.get("players", { token }) as unknown as PlayerI;
|
||||||
if (!plr) return await Player.fromUID(session, (await Account.fromToken(token))?.uid || Math.round(Math.random() * 50000));
|
if (!plr) return Player.fromUID(session, (await Account.fromToken(token))?.uid || Math.round(Math.random() * 50000));
|
||||||
|
|
||||||
return new Player(session, plr);
|
return new Player(session, plr);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getLineup(lineupIndex?: number): Promise<LineupInfo> {
|
public async getLineup(lineupIndex?: number): Promise<LineupInfo> {
|
||||||
// Get avatar data.
|
const curIndex = this.db.lineup.curIndex;
|
||||||
const index = lineupIndex ?? this.db.lineup.curIndex;
|
const lineup = this.db.lineup.lineups[lineupIndex || curIndex];
|
||||||
const lineup = this.db.lineup.lineups[index];
|
const avatars = await Avatar.fromLineup(this.uid, lineup);
|
||||||
const avatars = await Avatar.getAvatarsForLineup(this, lineup);
|
let slot = 0;
|
||||||
|
avatars.forEach(avatar => {
|
||||||
// Construct LineupInfo.
|
// Fallback lineup
|
||||||
const lineupAvatars : LineupAvatar[] = [];
|
if (!avatar.lineup) avatar.lineup = {
|
||||||
for (let slot = 0; slot < avatars.length; slot++) {
|
avatarType: AvatarType.AVATAR_FORMAL_TYPE,
|
||||||
lineupAvatars.push({
|
hp: 10000,
|
||||||
|
id: 1001,
|
||||||
|
satiety: 100,
|
||||||
slot: slot,
|
slot: slot,
|
||||||
avatarType: avatars[slot].db.avatarType,
|
sp: 10000
|
||||||
id: avatars[slot].db.baseAvatarId,
|
|
||||||
hp: avatars[slot].db.fightProps.hp,
|
|
||||||
sp: avatars[slot].db.fightProps.sp,
|
|
||||||
satiety: avatars[slot].db.fightProps.satiety
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
avatar.lineup.slot = slot++;
|
||||||
|
});
|
||||||
return {
|
return {
|
||||||
...lineup,
|
...lineup,
|
||||||
index: index,
|
avatarList: avatars.map(x => x.lineup)
|
||||||
avatarList: lineupAvatars
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -112,15 +103,6 @@ export default class Player {
|
|||||||
this.db.lineup.curIndex = curIndex;
|
this.db.lineup.curIndex = curIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getInventory() : Promise<Inventory> {
|
|
||||||
// If this players inventory has not been loaded yet, do so now.
|
|
||||||
if (!this.inventory) {
|
|
||||||
this.inventory = await Inventory.loadOrCreate(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.inventory;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async create(session: Session, uid: number | string): Promise<Player | undefined> {
|
public static async create(session: Session, uid: number | string): Promise<Player | undefined> {
|
||||||
if (typeof uid == "string") uid = Number(uid);
|
if (typeof uid == "string") uid = Number(uid);
|
||||||
const acc = await Account.fromUID(uid);
|
const acc = await Account.fromUID(uid);
|
||||||
@ -137,13 +119,13 @@ export default class Player {
|
|||||||
heroBasicType: HeroBasicType.BoyWarrior,
|
heroBasicType: HeroBasicType.BoyWarrior,
|
||||||
basicInfo: {
|
basicInfo: {
|
||||||
exp: 0,
|
exp: 0,
|
||||||
level: 70,
|
level: 1,
|
||||||
hcoin: 0,
|
hcoin: 0,
|
||||||
mcoin: 0,
|
mcoin: 0,
|
||||||
nickname: acc.name,
|
nickname: acc.name,
|
||||||
scoin: 0,
|
scoin: 0,
|
||||||
stamina: 180,
|
stamina: 100,
|
||||||
worldLevel: 6,
|
worldLevel: 1,
|
||||||
},
|
},
|
||||||
lineup: {
|
lineup: {
|
||||||
curIndex: 0,
|
curIndex: 0,
|
||||||
@ -159,34 +141,39 @@ export default class Player {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
banned: false
|
banned: false
|
||||||
} as PlayerI;
|
} as PlayerI
|
||||||
|
|
||||||
const LINEUPS = 6;
|
const baseLineup = {
|
||||||
for (let i = 0; i < LINEUPS; i++) {
|
|
||||||
const l : LineupI = {
|
|
||||||
avatarList: [1001],
|
avatarList: [1001],
|
||||||
extraLineupType: ExtraLineupType.LINEUP_NONE,
|
extraLineupType: ExtraLineupType.LINEUP_NONE,
|
||||||
index: i,
|
index: 0,
|
||||||
isVirtual: false,
|
isVirtual: false,
|
||||||
leaderSlot: 0,
|
leaderSlot: 0,
|
||||||
mp: 100,
|
mp: 100, // ?? Not sure what this is
|
||||||
name: `Team ${i}`,
|
name: "",
|
||||||
planeId: 10001
|
planeId: 10001
|
||||||
};
|
}
|
||||||
dataObj.lineup.lineups[i] = l;
|
const LINEUPS = 6;
|
||||||
|
let slot = 0;
|
||||||
|
dataObj.lineup = {
|
||||||
|
curIndex: 0,
|
||||||
|
lineups: {}
|
||||||
|
}
|
||||||
|
for (let i = 0; i <= LINEUPS; i++) {
|
||||||
|
const copy = baseLineup;
|
||||||
|
copy.index = slot++;
|
||||||
|
dataObj.lineup.lineups[i] = copy;
|
||||||
}
|
}
|
||||||
|
|
||||||
const player = new Player(session, dataObj);
|
await Avatar.create(uid, 1001, 0);
|
||||||
await Avatar.addAvatarToPlayer(player, 1001);
|
|
||||||
|
|
||||||
// Save to database and return.
|
|
||||||
await db.set("players", dataObj);
|
await db.set("players", dataObj);
|
||||||
return player;
|
return new Player(session, dataObj);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async save() {
|
public async save() {
|
||||||
const db = Database.getInstance();
|
const db = Database.getInstance();
|
||||||
c.debug(JSON.stringify(this.db, null, 2));
|
|
||||||
await db.update("players", { _id: this.db._id }, this.db);
|
await db.update("players", { _id: this.db._id }, this.db);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -13,18 +13,16 @@ 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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public despawnEntity(entityId: number, silent: boolean = false) {
|
public despawnEntity(entityId: number, silent: boolean = false) {
|
||||||
this.entities.delete(entityId);
|
this.entities.delete(entityId);
|
||||||
if (!silent) {
|
if (!silent) {
|
||||||
this.player.session.send(SceneEntityDisappearScNotify, {
|
this.player.session.send("SceneEntityDisappearScNotify", {
|
||||||
entityIdList: [entityId]
|
entityIdList: [entityId]
|
||||||
} as SceneEntityDisappearScNotify);
|
} as SceneEntityDisappearScNotify);
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import { Request, Response } from "express";
|
import { Request, Response } from "express";
|
||||||
import Account from "../../../../../../db/Account";
|
import Account from "../../../../../../db/Account";
|
||||||
import Config from "../../../../../../util/Config";
|
|
||||||
import Logger from "../../../../../../util/Logger";
|
import Logger from "../../../../../../util/Logger";
|
||||||
const c = new Logger("Dispatch");
|
const c = new Logger("Dispatch");
|
||||||
|
|
||||||
@ -21,14 +20,6 @@ export default async function handle(req: Request, res: Response) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!acc) {
|
if (!acc) {
|
||||||
if (Config.AUTO_ACCOUNT) {
|
|
||||||
const account = await Account.create(req.body.account);
|
|
||||||
c.log(`Account ${account.name} with UID ${account.uid} auto-created.`);
|
|
||||||
dataObj.data.account = account;
|
|
||||||
res.send(dataObj);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
dataObj.retcode = -202;
|
dataObj.retcode = -202;
|
||||||
dataObj.message = "Account not found";
|
dataObj.message = "Account not found";
|
||||||
c.warn(`Player ${req.body.account} not found (${req.ip})`);
|
c.warn(`Player ${req.body.account} not found (${req.ip})`);
|
||||||
|
@ -6,17 +6,12 @@
|
|||||||
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 Logger from "./util/Logger";
|
import Logger from "./util/Logger";
|
||||||
import ProtoFactory from "./util/ProtoFactory"
|
import ProtoFactory from "./util/ProtoFactory"
|
||||||
|
|
||||||
const c = new Logger("CrepeSR");
|
const c = new Logger("CrepeSR");
|
||||||
c.log(`Starting CrepeSR...`);
|
c.log(`Starting CrepeSR...`);
|
||||||
|
|
||||||
Banners.init();
|
|
||||||
ProtoFactory.init();
|
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,17 +37,36 @@ 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 {
|
||||||
public static fromEncodedBuffer(data: Buffer, name: PacketName): Buffer {
|
try {
|
||||||
const cmdid = CmdID[name];
|
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);
|
const packet = Buffer.allocUnsafe(16 + data.length);
|
||||||
packet.writeUInt32BE(0x1234567);
|
packet.writeUInt32BE(0x1234567);
|
||||||
packet.writeUint16BE(cmdid, 4);
|
packet.writeUint16BE(customCmdId || cmdid, 4);
|
||||||
packet.writeUint16BE(0, 6);
|
packet.writeUint16BE(0, 6);
|
||||||
packet.writeUint32BE(data.length, 8);
|
packet.writeUint32BE(data.length, 8);
|
||||||
data.copy(packet, 12);
|
data.copy(packet, 12);
|
||||||
packet.writeUint32BE(0x89abcdef, 12 + data.length);
|
packet.writeUint32BE(0x89abcdef, 12 + data.length);
|
||||||
return packet;
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,7 +40,8 @@ export default class SRServer {
|
|||||||
switch (handshake.handshakeType) {
|
switch (handshake.handshakeType) {
|
||||||
case HandshakeType.CONNECT:
|
case HandshakeType.CONNECT:
|
||||||
c.log(`${client} connected`);
|
c.log(`${client} connected`);
|
||||||
this.handshake(HandshakeType.SEND_BACK_CONV, rinfo);
|
const rsp = new Handshake(HandshakeType.SEND_BACK_CONV).encode();
|
||||||
|
this.udpSocket.send(rsp, 0, rsp.byteLength, rinfo.port, rinfo.address);
|
||||||
const kcpobj = new KCP(0x69, 0x420, {
|
const kcpobj = new KCP(0x69, 0x420, {
|
||||||
address: rinfo.address,
|
address: rinfo.address,
|
||||||
port: rinfo.port,
|
port: rinfo.port,
|
||||||
@ -49,7 +50,7 @@ export default class SRServer {
|
|||||||
kcpobj.nodelay(1, 5, 2, 0);
|
kcpobj.nodelay(1, 5, 2, 0);
|
||||||
kcpobj.output((d, s, u) => this.output(d, s, u));
|
kcpobj.output((d, s, u) => this.output(d, s, u));
|
||||||
kcpobj.wndsize(256, 256);
|
kcpobj.wndsize(256, 256);
|
||||||
this.sessions.set(client, new Session(kcpobj, rinfo, client));
|
this.sessions.set(client, new Session(kcpobj, rinfo));
|
||||||
break;
|
break;
|
||||||
case HandshakeType.DISCONNECT:
|
case HandshakeType.DISCONNECT:
|
||||||
c.log(`${client} disconnected`);
|
c.log(`${client} disconnected`);
|
||||||
@ -71,11 +72,6 @@ export default class SRServer {
|
|||||||
this.udpSocket.send(buf, 0, size, ctx.port, ctx.address);
|
this.udpSocket.send(buf, 0, size, ctx.port, ctx.address);
|
||||||
}
|
}
|
||||||
|
|
||||||
public handshake(hType: HandshakeType, rinfo: RemoteInfo) {
|
|
||||||
const rsp = new Handshake(hType).encode();
|
|
||||||
this.udpSocket.send(rsp, 0, rsp.byteLength, rinfo.port, rinfo.address);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async onError(err: Error) {
|
private async onError(err: Error) {
|
||||||
c.error(err);
|
c.error(err);
|
||||||
}
|
}
|
||||||
|
@ -8,37 +8,30 @@ import Logger, { VerboseLevel } from '../../util/Logger';
|
|||||||
import defaultHandler from '../packets/PacketHandler';
|
import defaultHandler from '../packets/PacketHandler';
|
||||||
import Account from '../../db/Account';
|
import Account from '../../db/Account';
|
||||||
import Player from '../../db/Player';
|
import Player from '../../db/Player';
|
||||||
import { BlackLimitLevel, PlayerKickOutScNotify, PlayerKickOutScNotify_KickType, PlayerSyncScNotify } from '../../data/proto/StarRail';
|
import { PlayerSyncScNotify } from '../../data/proto/StarRail';
|
||||||
import Avatar from '../../db/Avatar';
|
import Avatar from '../../db/Avatar';
|
||||||
import SRServer from './SRServer';
|
|
||||||
import { HandshakeType } from './Handshake';
|
|
||||||
import ProtoFactory, { MessageType } from '../../util/ProtoFactory';
|
|
||||||
|
|
||||||
function r(...args: string[]) {
|
function r(...args: string[]) {
|
||||||
return fs.readFileSync(resolve(__dirname, ...args));
|
return fs.readFileSync(resolve(__dirname, ...args));
|
||||||
}
|
}
|
||||||
type UnWrapMessageType<T> = T extends MessageType<infer U> ? U : T;
|
|
||||||
|
|
||||||
export default class Session {
|
export default class Session {
|
||||||
public key: Buffer = r('./initial.key');
|
public key: Buffer = r('./initial.key');
|
||||||
public c: Logger;
|
public c: Logger;
|
||||||
public account!: Account;
|
public account!: Account;
|
||||||
public player!: Player;
|
public player!: Player;
|
||||||
public kicked = false;
|
public constructor(private readonly kcpobj: KCP.KCP, public readonly ctx: RemoteInfo) {
|
||||||
|
this.kcpobj = kcpobj;
|
||||||
public constructor(private kcpobj: KCP.KCP, public readonly ctx: RemoteInfo, public id: string) {
|
|
||||||
this.ctx = ctx;
|
this.ctx = ctx;
|
||||||
this.c = new Logger(`${this.ctx.address}:${this.ctx.port}`, 'yellow');
|
this.c = new Logger(`${this.ctx.address}:${this.ctx.port}`, 'yellow');
|
||||||
this.update();
|
this.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
public inputRaw(data: Buffer) {
|
public inputRaw(data: Buffer) {
|
||||||
if (this.kicked) return;
|
|
||||||
this.kcpobj.input(data);
|
this.kcpobj.input(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async update() {
|
public async update() {
|
||||||
if (this.kicked) return;
|
|
||||||
if (!this.kcpobj) {
|
if (!this.kcpobj) {
|
||||||
console.error("wtf kcpobj is undefined");
|
console.error("wtf kcpobj is undefined");
|
||||||
console.debug(this)
|
console.debug(this)
|
||||||
@ -55,7 +48,7 @@ export default class Session {
|
|||||||
if (!recv) break;
|
if (!recv) break;
|
||||||
|
|
||||||
if (Packet.isValid(recv)) {
|
if (Packet.isValid(recv)) {
|
||||||
await this.handlePacket(new Packet(recv));
|
this.handlePacket(new Packet(recv));
|
||||||
}
|
}
|
||||||
|
|
||||||
} while (recv)
|
} while (recv)
|
||||||
@ -65,11 +58,10 @@ export default class Session {
|
|||||||
|
|
||||||
public async handlePacket(packet: Packet) {
|
public async handlePacket(packet: Packet) {
|
||||||
if (Logger.VERBOSE_LEVEL >= VerboseLevel.WARNS) this.c.log(packet.protoName)
|
if (Logger.VERBOSE_LEVEL >= VerboseLevel.WARNS) this.c.log(packet.protoName)
|
||||||
this.c.verbL(packet.body);
|
this.c.debug(packet.body);
|
||||||
this.c.verbH(packet.rawData);
|
|
||||||
|
|
||||||
await import(`../packets/${packet.protoName}`).then(async mod => {
|
import(`../packets/${packet.protoName}`).then(mod => {
|
||||||
await mod.default(this, packet);
|
mod.default(this, packet);
|
||||||
}).catch(e => {
|
}).catch(e => {
|
||||||
if (e.code === 'MODULE_NOT_FOUND') this.c.warn(`Unhandled packet: ${packet.protoName}`);
|
if (e.code === 'MODULE_NOT_FOUND') this.c.warn(`Unhandled packet: ${packet.protoName}`);
|
||||||
else this.c.error(e);
|
else this.c.error(e);
|
||||||
@ -79,53 +71,26 @@ export default class Session {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async sync() {
|
public async sync() {
|
||||||
const avatars = await Avatar.loadAvatarsForPlayer(this.player);
|
const avatars = await Avatar.fromUID(this.player.db._id);
|
||||||
const inventory = await this.player.getInventory();
|
this.send("PlayerSyncScNotify", {
|
||||||
|
|
||||||
this.send(PlayerSyncScNotify, PlayerSyncScNotify.fromPartial({
|
|
||||||
avatarSync: {
|
avatarSync: {
|
||||||
avatarList: avatars.map(x => x.asAvatarProto())
|
avatarList: avatars.map(x => x.data),
|
||||||
},
|
},
|
||||||
materialList: inventory.getMaterialList(),
|
|
||||||
equipmentList: inventory.getEquipmentList(),
|
|
||||||
relicList: inventory.getRelicsList(),
|
|
||||||
basicInfo: this.player.db.basicInfo
|
basicInfo: this.player.db.basicInfo
|
||||||
}));
|
} as PlayerSyncScNotify);
|
||||||
|
|
||||||
|
this.player.save();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async send<Class extends MessageType<any>,>(type: Class, data: UnWrapMessageType<Class>) {
|
public send(name: PacketName, body: {}) {
|
||||||
const typeName = ProtoFactory.getName(type);
|
this.c.debug(body);
|
||||||
const encodedBuffer = type.encode(type.fromPartial(data)).finish();
|
const packet = Packet.encode(name, body);
|
||||||
const packet = Packet.fromEncodedBuffer(Buffer.from(encodedBuffer), typeName);
|
if (!packet) return;
|
||||||
this.c.verbL(data);
|
if (Logger.VERBOSE_LEVEL >= VerboseLevel.WARNS) this.c.log(packet.protoName);
|
||||||
this.c.verbH(encodedBuffer);
|
this.kcpobj.send(packet.rawData);
|
||||||
if (!encodedBuffer) this.c.error("encodedBuffer is undefined");
|
|
||||||
if (Logger.VERBOSE_LEVEL >= VerboseLevel.WARNS) this.c.log(typeName);
|
|
||||||
//todo: might want to regen the ts-proto types with env = node
|
|
||||||
this.kcpobj.send(packet);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public kick(hard: boolean = true) {
|
|
||||||
SRServer.getInstance().sessions.delete(this.id);
|
|
||||||
this.kicked = true;
|
|
||||||
|
|
||||||
if (hard) this.send(PlayerKickOutScNotify, {
|
|
||||||
kickType: PlayerKickOutScNotify_KickType.KICK_BLACK,
|
|
||||||
blackInfo: {
|
|
||||||
limitLevel: BlackLimitLevel.BLACK_LIMIT_LEVEL_ALL,
|
|
||||||
beginTime: Math.round(Date.now() / 1000),
|
|
||||||
endTime: Math.round(Date.now() / 1000),
|
|
||||||
banType: 2
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
SRServer.getInstance().handshake(HandshakeType.DISCONNECT, this.ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public sendRaw(data: Buffer) {
|
public sendRaw(data: Buffer) {
|
||||||
if (this.kicked) return;
|
|
||||||
this.kcpobj.send(data);
|
this.kcpobj.send(data);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,88 +0,0 @@
|
|||||||
import { AvatarExpUpCsReq, AvatarExpUpScRsp } from "../../data/proto/StarRail";
|
|
||||||
import Avatar from "../../db/Avatar";
|
|
||||||
import { PayItemData } from "../../db/Inventory";
|
|
||||||
import AvatarExcel from "../../util/excel/AvatarExcel";
|
|
||||||
import AvatarExpItemExcel from "../../util/excel/AvatarExpItemExcel";
|
|
||||||
import AvatarPromotionExcel from "../../util/excel/AvatarPromotionExcel";
|
|
||||||
import ExpTypeExcel from "../../util/excel/ExpTypeExcel";
|
|
||||||
import Packet from "../kcp/Packet";
|
|
||||||
import Session from "../kcp/Session";
|
|
||||||
|
|
||||||
export default async function handle(session: Session, packet: Packet) {
|
|
||||||
const body = packet.body as AvatarExpUpCsReq;
|
|
||||||
const inventory = await session.player.getInventory();
|
|
||||||
|
|
||||||
// Get the target avatar.
|
|
||||||
const avatarId = body.baseAvatarId;
|
|
||||||
const avatar = await Avatar.loadAvatarForPlayer(session.player, avatarId);
|
|
||||||
const avatarExcelData = AvatarExcel.fromId(avatarId);
|
|
||||||
|
|
||||||
// Determine the next level cap based on the avatar's current promotion.
|
|
||||||
const levelCap = AvatarPromotionExcel.fromId(`${avatarId}:${avatar.db.promotion}`).MaxLevel;
|
|
||||||
|
|
||||||
// Determine the EXP we get from the consumed items.
|
|
||||||
let exp = 0;
|
|
||||||
const costMaterialList = [];
|
|
||||||
for (const item of body.itemCost!.itemList) {
|
|
||||||
// Determine amount of EXP given by that item.
|
|
||||||
// We know that the cost items given in this Req will be `PileItem`s.
|
|
||||||
const expPerItem = AvatarExpItemExcel.fromId(item.pileItem!.itemId).Exp;
|
|
||||||
|
|
||||||
// Add EXP for the number of items consumed.
|
|
||||||
exp += expPerItem * item.pileItem!.itemNum;
|
|
||||||
|
|
||||||
// Add material to cost.
|
|
||||||
costMaterialList.push({ id: item.pileItem!.itemId, count: item.pileItem!.itemNum } as PayItemData);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine cost, which is always 10% of EXP, and add to the list of cost materials.
|
|
||||||
const coinCost = exp * 0.1;
|
|
||||||
costMaterialList.push({ id: 2, count: coinCost } as PayItemData);
|
|
||||||
|
|
||||||
// Try consuming materials.
|
|
||||||
const success = await inventory.payItems(costMaterialList);
|
|
||||||
if (!success) {
|
|
||||||
// ToDo: Correct retcode.
|
|
||||||
session.send(AvatarExpUpScRsp, { retcode: 1, returnItemList: [] } as AvatarExpUpScRsp);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await inventory.save();
|
|
||||||
|
|
||||||
// Cost has been paid - now level up.
|
|
||||||
let currentAvatarExp = avatar.db.exp + exp;
|
|
||||||
let nextRequiredExp = ExpTypeExcel.fromId(`${avatarExcelData.ExpGroup}:${avatar.db.level}`).Exp;
|
|
||||||
while (currentAvatarExp >= nextRequiredExp && avatar.db.level < levelCap) {
|
|
||||||
// Increase level.
|
|
||||||
avatar.db.level++;
|
|
||||||
|
|
||||||
// Deduct EXP necessary for this level.
|
|
||||||
currentAvatarExp -= nextRequiredExp;
|
|
||||||
|
|
||||||
// Determine EXP necessary for the next level.
|
|
||||||
nextRequiredExp = ExpTypeExcel.fromId(`${avatarExcelData.ExpGroup}:${avatar.db.level}`).Exp;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate the character's new EXP and any excess EXP.
|
|
||||||
let excessExp = 0;
|
|
||||||
if (avatar.db.level == levelCap) {
|
|
||||||
avatar.db.exp = 0;
|
|
||||||
excessExp = currentAvatarExp;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
avatar.db.exp = currentAvatarExp;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save.
|
|
||||||
await avatar.save();
|
|
||||||
|
|
||||||
// ToDo: Handle return items.
|
|
||||||
|
|
||||||
// Done. Sync and send response.
|
|
||||||
await session.sync();
|
|
||||||
|
|
||||||
session.send(AvatarExpUpScRsp, {
|
|
||||||
retcode: 0,
|
|
||||||
returnItemList: []
|
|
||||||
} as AvatarExpUpScRsp);
|
|
||||||
}
|
|
@ -5,7 +5,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 ChangeLineupLeaderCsReq;
|
const body = packet.body as ChangeLineupLeaderCsReq;
|
||||||
|
|
||||||
session.send(ChangeLineupLeaderScRsp, {
|
session.send("ChangeLineupLeaderScRsp", {
|
||||||
retcode: 0,
|
retcode: 0,
|
||||||
slot: body.slot
|
slot: body.slot
|
||||||
} as ChangeLineupLeaderScRsp);
|
} as ChangeLineupLeaderScRsp);
|
||||||
|
@ -1,47 +0,0 @@
|
|||||||
import { DoGachaCsReq, DoGachaScRsp, GachaItem, Item, ItemList } from "../../data/proto/StarRail";
|
|
||||||
import { PayItemData } from "../../db/Inventory";
|
|
||||||
import Banners from "../../util/Banner";
|
|
||||||
import Packet from "../kcp/Packet";
|
|
||||||
import Session from "../kcp/Session";
|
|
||||||
|
|
||||||
export default async function handle(session: Session, packet: Packet) {
|
|
||||||
const gachaItemList: GachaItem[] = [];
|
|
||||||
const body = packet.body as DoGachaCsReq;
|
|
||||||
const banner = Banners.config.find(banner => banner.gachaId === body.gachaId)!;
|
|
||||||
const combined = banner.rateUpItems4.concat(banner.rateUpItems5)
|
|
||||||
|
|
||||||
// Pay currency.
|
|
||||||
const inventory = await session.player.getInventory();
|
|
||||||
const success = await inventory.payItems([{ id: banner.costItemId, count: body.gachaNum } as PayItemData]);
|
|
||||||
|
|
||||||
if (!success) {
|
|
||||||
session.send(DoGachaScRsp, {
|
|
||||||
retcode: 1301
|
|
||||||
} as DoGachaScRsp);
|
|
||||||
}
|
|
||||||
|
|
||||||
//bad gachaing but whatever....
|
|
||||||
//TODO: pity system, proper logic
|
|
||||||
for(let i = 0; i < body.gachaNum; i++){
|
|
||||||
const result = combined[Math.floor(Math.random() * combined.length)];
|
|
||||||
gachaItemList.push({
|
|
||||||
gachaItem: {
|
|
||||||
itemId: result,
|
|
||||||
num: 1
|
|
||||||
} as Item,
|
|
||||||
tokenItem: {},
|
|
||||||
transferItemList: {},
|
|
||||||
isNew: true //TODO: avatar checking
|
|
||||||
} as GachaItem);
|
|
||||||
}
|
|
||||||
session.send(DoGachaScRsp, {
|
|
||||||
retcode: 0,
|
|
||||||
gachaId: body.gachaId!,
|
|
||||||
gachaNum: body.gachaNum!,
|
|
||||||
newGachaRandom: body.gachaRandom!,
|
|
||||||
newbieGachaCnt: 0,
|
|
||||||
todayGachaCnt: 0,
|
|
||||||
todayTotalGachaCnt: 0, //todo find out what are THESE
|
|
||||||
gachaItemList: gachaItemList
|
|
||||||
} as DoGachaScRsp);
|
|
||||||
}
|
|
@ -1,16 +0,0 @@
|
|||||||
import { DressAvatarCsReq, DressAvatarScRsp } from "../../data/proto/StarRail";
|
|
||||||
import Avatar from "../../db/Avatar";
|
|
||||||
import Packet from "../kcp/Packet";
|
|
||||||
import Session from "../kcp/Session";
|
|
||||||
|
|
||||||
export default async function handle(session: Session, packet: Packet) {
|
|
||||||
const body = packet.body as DressAvatarCsReq;
|
|
||||||
|
|
||||||
const avatar = await Avatar.loadAvatarForPlayer(session.player, body.baseAvatarId);
|
|
||||||
|
|
||||||
avatar.db.equipmentUniqueId = body.equipmentUniqueId;
|
|
||||||
await avatar.save();
|
|
||||||
|
|
||||||
session.send(DressAvatarScRsp, { retcode: 0 });
|
|
||||||
session.sync();
|
|
||||||
}
|
|
@ -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,25 +6,17 @@ 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.fromEntryId(body.entryId);
|
||||||
const mazePlane = MazePlaneExcel.fromPlaneId(mazeEntry.PlaneID);
|
|
||||||
|
|
||||||
let curLineup = await session.player.getLineup();
|
session.send("EnterMazeScRsp", {
|
||||||
curLineup.planeId = mazeEntry.PlaneID;
|
|
||||||
session.player.setLineup(curLineup);
|
|
||||||
session.player.db.posData.floorID = mazeEntry.FloorID;
|
|
||||||
session.player.db.posData.planeID = mazeEntry.PlaneID;
|
|
||||||
session.player.save();
|
|
||||||
|
|
||||||
session.send(EnterMazeScRsp, {
|
|
||||||
retcode: 0,
|
retcode: 0,
|
||||||
maze: {
|
maze: {
|
||||||
floor: {
|
floor: {
|
||||||
floorId: mazeEntry.FloorID,
|
floorId: mazeEntry.StartFloorID,
|
||||||
scene: {
|
scene: {
|
||||||
planeId: mazeEntry.PlaneID,
|
planeId: mazeEntry.PlaneID,
|
||||||
floorId: mazeEntry.FloorID,
|
floorId: mazeEntry.StartFloorID,
|
||||||
gameModeType: MazePlaneExcel.getGameModeForPlaneType(mazePlane.PlaneType),
|
gameModeType: 1,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
id: mazeEntry.PlaneID,
|
id: mazeEntry.PlaneID,
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import { EnterSectionScRsp } from "../../data/proto/StarRail";
|
|
||||||
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) {
|
||||||
session.send(EnterSectionScRsp, { retcode: 0 });
|
session.send("EnterSectionScRsp", { retcode: 0 });
|
||||||
}
|
}
|
@ -1,7 +1,6 @@
|
|||||||
import { EntityBindPropScRsp } from "../../data/proto/StarRail";
|
|
||||||
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) {
|
||||||
session.send(EntityBindPropScRsp, { retcode: 0 });
|
session.send("EntityBindPropScRsp", { retcode: 0 });
|
||||||
}
|
}
|
@ -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);
|
|
||||||
}
|
|
@ -5,7 +5,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 FinishTalkMissionCsReq;
|
const body = packet.body as FinishTalkMissionCsReq;
|
||||||
|
|
||||||
session.send(FinishTalkMissionScRsp, {
|
session.send("FinishTalkMissionScRsp", {
|
||||||
retcode: 0,
|
retcode: 0,
|
||||||
talkStr: body.talkStr
|
talkStr: body.talkStr
|
||||||
} as FinishTalkMissionScRsp);
|
} as FinishTalkMissionScRsp);
|
||||||
|
@ -14,5 +14,5 @@ export default async function handle(session: Session, packet: Packet) {
|
|||||||
reward: { itemList: [] }
|
reward: { itemList: [] }
|
||||||
}
|
}
|
||||||
|
|
||||||
session.send(FinishTutorialGuideScRsp, dataObj);
|
session.send("FinishTutorialGuideScRsp", dataObj);
|
||||||
}
|
}
|
@ -18,5 +18,5 @@ export default async function handle(session: Session, packet: Packet) {
|
|||||||
lineupList
|
lineupList
|
||||||
} as GetAllLineupDataScRsp;
|
} as GetAllLineupDataScRsp;
|
||||||
|
|
||||||
session.send(GetAllLineupDataScRsp, dataObj);
|
session.send("GetAllLineupDataScRsp", dataObj);
|
||||||
}
|
}
|
@ -1,19 +1,23 @@
|
|||||||
import { GetAvatarDataCsReq, GetAvatarDataScRsp } from "../../data/proto/StarRail";
|
import { GetAvatarDataCsReq, GetAvatarDataScRsp } from "../../data/proto/StarRail";
|
||||||
|
import AvatarExcelTable from "../../data/excel/AvatarExcelTable.json";
|
||||||
import Packet from "../kcp/Packet";
|
import Packet from "../kcp/Packet";
|
||||||
import Session from "../kcp/Session";
|
import Session from "../kcp/Session";
|
||||||
import AvatarDb from "../../db/Avatar";
|
import Avatar from "../../db/Avatar";
|
||||||
|
|
||||||
export default async function handle(session: Session, packet: Packet) {
|
export default async function handle(session: Session, packet: Packet) {
|
||||||
const body = packet.body as GetAvatarDataCsReq;
|
const body = packet.body as GetAvatarDataCsReq;
|
||||||
const avatars = await AvatarDb.loadAvatarsForPlayer(session.player);
|
|
||||||
|
const avatar = await Avatar.fromUID(session.player.db._id);
|
||||||
|
|
||||||
const dataObj = {
|
const dataObj = {
|
||||||
retcode: 0,
|
retcode: 0,
|
||||||
avatarList: avatars.map(av => av.asAvatarProto()),
|
avatarList: avatar.map(av => av.data),
|
||||||
isAll: body.isGetAll
|
isAll: body.isGetAll
|
||||||
};
|
} as GetAvatarDataScRsp;
|
||||||
|
|
||||||
// Make sure we wait for this to send.
|
Object.values(AvatarExcelTable).forEach(avatar => {
|
||||||
// GetAvatarDataScRsp HAS to be sent immediately after the Req.
|
// dataObj.avatarList.push()
|
||||||
await session.send(GetAvatarDataScRsp, dataObj);
|
});
|
||||||
|
|
||||||
|
session.send("GetAvatarDataScRsp", dataObj);
|
||||||
}
|
}
|
@ -1,14 +1,12 @@
|
|||||||
import { Equipment, GetBagScRsp, Material, Relic } from "../../data/proto/StarRail";
|
import { GetBagScRsp } from "../../data/proto/StarRail";
|
||||||
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) {
|
||||||
const inventory = await session.player.getInventory();
|
session.send("GetBagScRsp", {
|
||||||
|
equipmentList: [],
|
||||||
session.send(GetBagScRsp, {
|
materialList: [],
|
||||||
equipmentList: inventory.getEquipmentList(),
|
relicList: [],
|
||||||
materialList: inventory.getMaterialList(),
|
|
||||||
relicList: inventory.getRelicsList(),
|
|
||||||
retcode: 0,
|
retcode: 0,
|
||||||
rogueItemList: [],
|
rogueItemList: [],
|
||||||
waitDelResourceList: []
|
waitDelResourceList: []
|
||||||
|
@ -3,7 +3,7 @@ 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) {
|
||||||
session.send(GetBasicInfoScRsp, {
|
session.send("GetBasicInfoScRsp", {
|
||||||
curDay: 1,
|
curDay: 1,
|
||||||
exchangeTimes: 0,
|
exchangeTimes: 0,
|
||||||
retcode: 0,
|
retcode: 0,
|
||||||
|
@ -19,5 +19,5 @@ export default async function handle(session: Session, packet: Packet) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
session.send(GetChallengeScRsp, dataObj);
|
session.send("GetChallengeScRsp", dataObj);
|
||||||
}
|
}
|
@ -19,5 +19,5 @@ export default async function handle(session: Session, packet: Packet) {
|
|||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
|
||||||
session.send(GetChallengeRaidInfoScRsp, dataObj);
|
session.send("GetChallengeRaidInfoScRsp", dataObj);
|
||||||
}
|
}
|
@ -5,7 +5,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 lineup = await session.player.getLineup();
|
const lineup = await session.player.getLineup();
|
||||||
|
|
||||||
session.send(GetCurBattleInfoScRsp, {
|
session.send("GetCurBattleInfoScRsp", {
|
||||||
retcode: 0,
|
retcode: 0,
|
||||||
avatarList: lineup.avatarList.map(list => {
|
avatarList: lineup.avatarList.map(list => {
|
||||||
return {
|
return {
|
||||||
|
@ -1,12 +1,39 @@
|
|||||||
import { AvatarType, ExtraLineupType, GetCurLineupDataScRsp } from "../../data/proto/StarRail";
|
import { AvatarType, ExtraLineupType, GetCurLineupDataScRsp } from "../../data/proto/StarRail";
|
||||||
|
import Avatar from "../../db/Avatar";
|
||||||
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) {
|
||||||
let lineup = await session.player.getLineup();
|
let lineup = await session.player.getLineup();
|
||||||
|
|
||||||
session.send(GetCurLineupDataScRsp, {
|
// This is a HORRIBLE solution, but tbh I just can't reproduce the bug so:
|
||||||
|
if (!lineup) {
|
||||||
|
session.c.error("Error! lineup is undefined. Falling back to default lineup.", false);
|
||||||
|
lineup = {
|
||||||
|
avatarList: [{
|
||||||
|
avatarType: AvatarType.AVATAR_FORMAL_TYPE,
|
||||||
|
hp: 10000,
|
||||||
|
id: 1001,
|
||||||
|
sp: 10000,
|
||||||
|
satiety: 100,
|
||||||
|
slot: 0,
|
||||||
|
}],
|
||||||
|
extraLineupType: ExtraLineupType.LINEUP_NONE,
|
||||||
|
index: 0,
|
||||||
|
isVirtual: false,
|
||||||
|
leaderSlot: 0,
|
||||||
|
mp: 100,
|
||||||
|
planeId: 10001,
|
||||||
|
name: "Fallback"
|
||||||
|
}
|
||||||
|
session.player.setLineup(lineup, 0, 0);
|
||||||
|
session.player.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
session.send("GetCurLineupDataScRsp", {
|
||||||
retcode: 0,
|
retcode: 0,
|
||||||
lineup: lineup
|
lineup: {
|
||||||
|
...lineup,
|
||||||
|
}
|
||||||
} as GetCurLineupDataScRsp);
|
} as GetCurLineupDataScRsp);
|
||||||
}
|
}
|
@ -1,43 +1,60 @@
|
|||||||
import { GetCurSceneInfoScRsp, MotionInfo, SceneEntityInfo, SceneNpcMonsterInfo, StartCocoonStageCsReq, Vector } from "../../data/proto/StarRail";
|
import { DailyTaskDataScNotify, 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 DailyMissionDataExcel from "../../util/excel/DailyMissionDataExcel";
|
||||||
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);
|
||||||
|
session.player.scene.spawnEntity(curAvatarEntity, true);
|
||||||
|
session.player.scene.entryId = 10001;
|
||||||
|
|
||||||
// Send response.
|
// Daily Tasks
|
||||||
session.send(GetCurSceneInfoScRsp, dataObj);
|
const dailyTasks = DailyMissionDataExcel.random();
|
||||||
|
|
||||||
|
const dataObj: DailyTaskDataScNotify = {
|
||||||
|
dailyTaskList: [{
|
||||||
|
mainMissionId: 3010201,
|
||||||
|
isFinished: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mainMissionId: 3010202,
|
||||||
|
isFinished: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mainMissionId: 3010203,
|
||||||
|
isFinished: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mainMissionId: 3010204,
|
||||||
|
isFinished: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mainMissionId: 3010205,
|
||||||
|
isFinished: false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
isTakenExtraReward: false,
|
||||||
|
finishedNum: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
session.send("DailyTaskDataScNotify", dataObj);
|
||||||
}
|
}
|
@ -3,7 +3,7 @@ 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) {
|
||||||
session.send(GetDialogueEventDataScRsp, {
|
session.send("GetDialogueEventDataScRsp", {
|
||||||
dialogueEventList: [],
|
dialogueEventList: [],
|
||||||
retcode: 0
|
retcode: 0
|
||||||
} as GetDialogueEventDataScRsp);
|
} as GetDialogueEventDataScRsp);
|
||||||
|
@ -3,7 +3,7 @@ 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) {
|
||||||
session.send(GetExpeditionDataScRsp, {
|
session.send("GetExpeditionDataScRsp", {
|
||||||
retcode: 0,
|
retcode: 0,
|
||||||
expedtionList: [],
|
expedtionList: [],
|
||||||
unlockedExpeditionIdList: [],
|
unlockedExpeditionIdList: [],
|
||||||
|
@ -19,5 +19,5 @@ export default async function handle(session: Session, packet: Packet) {
|
|||||||
dataObj.npcMeetStatusList.push(meetStatusObj);
|
dataObj.npcMeetStatusList.push(meetStatusObj);
|
||||||
});
|
});
|
||||||
|
|
||||||
session.send(GetFirstTalkNpcScRsp, dataObj);
|
session.send("GetFirstTalkNpcScRsp", dataObj);
|
||||||
}
|
}
|
@ -1,23 +1,22 @@
|
|||||||
import { GachaInfo, GetGachaInfoCsReq, GetGachaInfoScRsp } from "../../data/proto/StarRail";
|
import { GetGachaInfoScRsp } from "../../data/proto/StarRail";
|
||||||
import Packet from "../kcp/Packet";
|
import Packet from "../kcp/Packet";
|
||||||
import Session from "../kcp/Session";
|
import Session from "../kcp/Session";
|
||||||
import Banner from './../../util/Banner';
|
|
||||||
|
const unix = () => Math.floor(Date.now() / 1000);
|
||||||
|
|
||||||
export default async function handle(session: Session, packet: Packet) {
|
export default async function handle(session: Session, packet: Packet) {
|
||||||
session.send(GetGachaInfoScRsp, {
|
session.send("GetGachaInfoScRsp", {
|
||||||
gachaRandom: 0,
|
gachaRandom: 2503,
|
||||||
retcode: 0,
|
retcode: 0,
|
||||||
gachaInfoList: Banner.config.map(banner => {
|
gachaInfoList: [{
|
||||||
return {
|
beginTime: unix(),
|
||||||
beginTime: 0,
|
endTime: unix() * 2,
|
||||||
endTime: 1924992000,
|
newbieGachaCnt: 10,
|
||||||
gachaId: banner.gachaId,
|
todayGachaCnt: 1,
|
||||||
detailWebview: banner.detailWebview,
|
gachaId: 763, // TODO: Figure out gachaIDs
|
||||||
newbieGachaCnt: 0,
|
detailWebview: "https://omfgdogs.com/"
|
||||||
todayGachaCnt: 0
|
}],
|
||||||
} as GachaInfo
|
|
||||||
}),
|
|
||||||
todaySingleGachaMaxCnt: 10,
|
todaySingleGachaMaxCnt: 10,
|
||||||
todayTotalGachaCnt: 10,
|
todayTotalGachaCnt: 1,
|
||||||
} as GetGachaInfoScRsp);
|
} as GetGachaInfoScRsp);
|
||||||
}
|
}
|
@ -5,7 +5,7 @@ import Session from "../kcp/Session";
|
|||||||
export default async function handle(session: Session, packet: Packet) {
|
export default async function handle(session: Session, packet: Packet) {
|
||||||
let gender: Gender = (session.player.db.heroBasicType % 2 === 0) ? Gender.GenderWoman : Gender.GenderMan;
|
let gender: Gender = (session.player.db.heroBasicType % 2 === 0) ? Gender.GenderWoman : Gender.GenderMan;
|
||||||
|
|
||||||
session.send(GetHeroBasicTypeInfoScRsp, {
|
session.send("GetHeroBasicTypeInfoScRsp", {
|
||||||
retcode: 0,
|
retcode: 0,
|
||||||
gender: gender,
|
gender: gender,
|
||||||
basicTypeInfoList: [{
|
basicTypeInfoList: [{
|
||||||
|
@ -3,7 +3,7 @@ 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) {
|
||||||
session.send(GetHeroPathScRsp, {
|
session.send("GetHeroPathScRsp", {
|
||||||
retcode: 0,
|
retcode: 0,
|
||||||
heroPathList: []
|
heroPathList: []
|
||||||
} as GetHeroPathScRsp);
|
} as GetHeroPathScRsp);
|
||||||
|
@ -3,7 +3,7 @@ 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) {
|
||||||
session.send(GetLevelRewardTakenListScRsp, {
|
session.send("GetLevelRewardTakenListScRsp", {
|
||||||
retcode: 0,
|
retcode: 0,
|
||||||
takenLevelList: []
|
takenLevelList: []
|
||||||
} as GetLevelRewardTakenListScRsp);
|
} as GetLevelRewardTakenListScRsp);
|
||||||
|
@ -1,46 +0,0 @@
|
|||||||
import { AvatarType, ExtraLineupType, GetLineupAvatarDataScRsp } from "../../data/proto/StarRail";
|
|
||||||
import Packet from "../kcp/Packet";
|
|
||||||
import Session from "../kcp/Session";
|
|
||||||
|
|
||||||
export default async function handle(session: Session, packet: Packet) {
|
|
||||||
let lineup = await session.player.getLineup();
|
|
||||||
|
|
||||||
// This is a HORRIBLE solution, but tbh I just can't reproduce the bug so:
|
|
||||||
if (!lineup) {
|
|
||||||
session.c.error("Error! lineup is undefined. Falling back to default lineup.", false);
|
|
||||||
lineup = {
|
|
||||||
avatarList: [{
|
|
||||||
avatarType: AvatarType.AVATAR_FORMAL_TYPE,
|
|
||||||
hp: 10000,
|
|
||||||
id: 1001,
|
|
||||||
sp: 10000,
|
|
||||||
satiety: 100,
|
|
||||||
slot: 0,
|
|
||||||
}],
|
|
||||||
extraLineupType: ExtraLineupType.LINEUP_NONE,
|
|
||||||
index: 0,
|
|
||||||
isVirtual: false,
|
|
||||||
leaderSlot: 0,
|
|
||||||
mp: 100,
|
|
||||||
planeId: 10001,
|
|
||||||
name: "Fallback"
|
|
||||||
}
|
|
||||||
session.player.setLineup(lineup, 0, 0);
|
|
||||||
session.player.save();
|
|
||||||
}
|
|
||||||
|
|
||||||
const dataObj: GetLineupAvatarDataScRsp = {
|
|
||||||
retcode: 0,
|
|
||||||
avatarDataList: []
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const avatar of lineup.avatarList) {
|
|
||||||
dataObj.avatarDataList.push({
|
|
||||||
avatarType: avatar.avatarType,
|
|
||||||
hp: avatar.hp,
|
|
||||||
id: avatar.id,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
session.send(GetLineupAvatarDataScRsp, dataObj);
|
|
||||||
}
|
|
@ -3,7 +3,7 @@ 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) {
|
||||||
session.send(GetLoginActivityScRsp, {
|
session.send("GetLoginActivityScRsp", {
|
||||||
retcode: 0,
|
retcode: 0,
|
||||||
loginActivityList: [{
|
loginActivityList: [{
|
||||||
id: 1001,
|
id: 1001,
|
||||||
|
@ -3,7 +3,7 @@ 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) {
|
||||||
session.send(GetMailScRsp, {
|
session.send("GetMailScRsp", {
|
||||||
retcode: 0,
|
retcode: 0,
|
||||||
mailList: [],
|
mailList: [],
|
||||||
noticeMailList: [],
|
noticeMailList: [],
|
||||||
|
@ -1,31 +1,24 @@
|
|||||||
import { GetMazeMapInfoCsReq, GetMazeMapInfoScRsp } from "../../data/proto/StarRail";
|
import { GetMazeMapInfoScRsp } from "../../data/proto/StarRail";
|
||||||
import MapEntryExcel from "../../util/excel/MapEntryExcel";
|
|
||||||
import MappingInfoExcel from "../../util/excel/MappingInfoExcel";
|
|
||||||
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) {
|
||||||
const body = packet.body as GetMazeMapInfoCsReq;
|
const body = packet.body as GetMazeMapInfoScRsp;
|
||||||
|
|
||||||
const mapping = MappingInfoExcel.fromId(body.entryId, session.player.db.basicInfo.worldLevel) || MappingInfoExcel.fromId(1001);
|
|
||||||
|
|
||||||
const dataObj = {
|
const dataObj = {
|
||||||
retcode: 0,
|
retcode: 0,
|
||||||
entryId: body.entryId,
|
entryId: body.entryId,
|
||||||
lightenSectionList: [],
|
lightenSectionList: [],
|
||||||
mazePropList: [{ groupId: mapping.GroupID, configId: mapping.ConfigID, state: 0 }],
|
mazePropList: [{ groupId: 0, configId: 0, state: 0 }],
|
||||||
mazeGroupList: [{ groupId: mapping.GroupID, modifyTime: 0 }],
|
mazeGroupList: [{ groupId: 0, modifyTime: 0 }],
|
||||||
opendChestNum: 69,
|
opendChestNum: 0,
|
||||||
unlockTeleportList: []
|
unlockTeleportList: []
|
||||||
} as GetMazeMapInfoScRsp;
|
} as GetMazeMapInfoScRsp;
|
||||||
|
|
||||||
// TODO: No excel info atm
|
// TODO: No excel info atm
|
||||||
for (let i = 0; i < 500; i++) {
|
for (let i = 0; i < 20; i++) {
|
||||||
dataObj.lightenSectionList.push(i)
|
dataObj.lightenSectionList.push(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
dataObj.unlockTeleportList = MapEntryExcel.all().map(x => x.ID);
|
session.send("GetMazeMapInfoScRsp", dataObj);
|
||||||
|
|
||||||
session.send(GetMazeMapInfoScRsp, dataObj);
|
|
||||||
}
|
}
|
@ -3,7 +3,7 @@ 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) {
|
||||||
session.send(GetMazeTimeOfDayScRsp, {
|
session.send("GetMazeTimeOfDayScRsp", {
|
||||||
retcode: 0,
|
retcode: 0,
|
||||||
mazeTimeOfDayMap: {}
|
mazeTimeOfDayMap: {}
|
||||||
} as GetMazeTimeOfDayScRsp);
|
} as GetMazeTimeOfDayScRsp);
|
||||||
|
@ -1,10 +1,34 @@
|
|||||||
import { GetMissionDataScRsp } from "../../data/proto/StarRail";
|
import { GetMissionDataScRsp, MissionStatus } from "../../data/proto/StarRail";
|
||||||
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) {
|
||||||
session.send(GetMissionDataScRsp, {
|
session.send("GetMissionDataScRsp", {
|
||||||
retcode: 0,
|
retcode: 0,
|
||||||
missionList: []
|
missionList: [{
|
||||||
|
id: 3010201,
|
||||||
|
progress: 0,
|
||||||
|
status: MissionStatus.MISSION_DOING
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3010202,
|
||||||
|
progress: 0,
|
||||||
|
status: MissionStatus.MISSION_DOING
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3010203,
|
||||||
|
progress: 0,
|
||||||
|
status: MissionStatus.MISSION_DOING
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3010204,
|
||||||
|
progress: 0,
|
||||||
|
status: MissionStatus.MISSION_DOING
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3010205,
|
||||||
|
progress: 0,
|
||||||
|
status: MissionStatus.MISSION_DOING
|
||||||
|
}]
|
||||||
} as GetMissionDataScRsp);
|
} as GetMissionDataScRsp);
|
||||||
}
|
}
|
@ -3,7 +3,7 @@ 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) {
|
||||||
session.send(GetMissionEventDataScRsp, {
|
session.send("GetMissionEventDataScRsp", {
|
||||||
retcode: 0,
|
retcode: 0,
|
||||||
missionEventList: []
|
missionEventList: []
|
||||||
} as unknown as GetMissionEventDataScRsp);
|
} as unknown as GetMissionEventDataScRsp);
|
||||||
|
@ -42,7 +42,7 @@ export default async function handle(session: Session, packet: Packet) {
|
|||||||
dataObj.missionEventStatusList.push({
|
dataObj.missionEventStatusList.push({
|
||||||
id: id,
|
id: id,
|
||||||
progress: 0,
|
progress: 0,
|
||||||
status: MissionStatus.MISSION_FINISH
|
status: MissionStatus.MISSION_DOING
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -50,9 +50,9 @@ export default async function handle(session: Session, packet: Packet) {
|
|||||||
dataObj.subMissionStatusList.push({
|
dataObj.subMissionStatusList.push({
|
||||||
id: id,
|
id: id,
|
||||||
progress: 0,
|
progress: 0,
|
||||||
status: MissionStatus.MISSION_FINISH
|
status: MissionStatus.MISSION_DOING
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
session.send(GetMissionStatusScRsp, dataObj);
|
session.send("GetMissionStatusScRsp", dataObj);
|
||||||
}
|
}
|
@ -10,5 +10,5 @@ export default async function handle(session: Session, packet: Packet) {
|
|||||||
messageGroupList: [],
|
messageGroupList: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
session.send(GetNpcMessageGroupScRsp, dataObj);
|
session.send("GetNpcMessageGroupScRsp", dataObj);
|
||||||
}
|
}
|
@ -3,7 +3,7 @@ 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) {
|
||||||
session.send(GetNpcStatusScRsp, {
|
session.send("GetNpcStatusScRsp", {
|
||||||
retcode: 0,
|
retcode: 0,
|
||||||
messageStatusList: []
|
messageStatusList: []
|
||||||
} as GetNpcStatusScRsp);
|
} as GetNpcStatusScRsp);
|
||||||
|
@ -5,7 +5,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 GetNpcTakenRewardCsReq;
|
const body = packet.body as GetNpcTakenRewardCsReq;
|
||||||
|
|
||||||
session.send(GetNpcTakenRewardScRsp, {
|
session.send("GetNpcTakenRewardScRsp", {
|
||||||
retcode: 0,
|
retcode: 0,
|
||||||
npcId: body.npcId,
|
npcId: body.npcId,
|
||||||
talkEventList: []
|
talkEventList: []
|
||||||
|
@ -3,7 +3,7 @@ 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) {
|
||||||
session.send(GetPrestigeInfoScRsp, {
|
session.send("GetPrestigeInfoScRsp", {
|
||||||
retcode: 0,
|
retcode: 0,
|
||||||
prestigeInfo: {
|
prestigeInfo: {
|
||||||
exp: 0,
|
exp: 0,
|
||||||
|
@ -3,7 +3,7 @@ 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) {
|
||||||
session.send(GetQuestDataScRsp, {
|
session.send("GetQuestDataScRsp", {
|
||||||
questList: [],
|
questList: [],
|
||||||
retcode: 0,
|
retcode: 0,
|
||||||
takenAchievementLevelList: [],
|
takenAchievementLevelList: [],
|
||||||
|
@ -3,7 +3,7 @@ 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) {
|
||||||
session.send(GetRogueInfoScRsp, {
|
session.send("GetRogueInfoScRsp", {
|
||||||
retcode: 0,
|
retcode: 0,
|
||||||
rogueInfo: {
|
rogueInfo: {
|
||||||
status: RogueStatus.ROGUE_STATUS_NONE,
|
status: RogueStatus.ROGUE_STATUS_NONE,
|
||||||
|
@ -37,5 +37,5 @@ export default async function handle(session: Session, packet: Packet) {
|
|||||||
dataObj.shopList.push(shopObj);
|
dataObj.shopList.push(shopObj);
|
||||||
});
|
});
|
||||||
|
|
||||||
session.send(GetShopListScRsp, dataObj);
|
session.send("GetShopListScRsp", dataObj);
|
||||||
}
|
}
|
@ -3,7 +3,7 @@ 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) {
|
||||||
session.send(GetSpringRecoverDataScRsp, {
|
session.send("GetSpringRecoverDataScRsp", {
|
||||||
retcode: 0,
|
retcode: 0,
|
||||||
healPoolInfo: {
|
healPoolInfo: {
|
||||||
healPool: 0,
|
healPool: 0,
|
||||||
|
@ -18,5 +18,5 @@ export default async function handle(session: Session, packet: Packet) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
session.send(GetTutorialScRsp, dataObj);
|
session.send("GetTutorialScRsp", dataObj);
|
||||||
}
|
}
|
@ -18,5 +18,5 @@ export default async function handle(session: Session, packet: Packet) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
session.send(GetTutorialGuideScRsp, dataObj);
|
session.send("GetTutorialGuideScRsp", dataObj);
|
||||||
}
|
}
|
@ -1,26 +1,35 @@
|
|||||||
import {
|
import { AvatarType, JoinLineupCsReq, 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.
|
let lineup = await session.player.getLineup();
|
||||||
const slot = body.slot ?? 0;
|
const slot = body.slot || 0;
|
||||||
const index = body.index ?? 1;
|
const avatarList = [];
|
||||||
|
for (const avatarId in lineup) {
|
||||||
|
const avatar = await Avatar.fromUID(session.player.db._id, Number(avatarId));
|
||||||
|
if (avatar.length === 0) return session.c.warn(`Avatar ${body.baseAvatarId} not found`);
|
||||||
|
if (avatar) avatarList.push(avatar[0]);
|
||||||
|
}
|
||||||
|
|
||||||
session.player.db.lineup.lineups[index].avatarList[slot] = body.baseAvatarId;
|
lineup.avatarList[slot] = {
|
||||||
await session.player.save();
|
avatarType: AvatarType.AVATAR_FORMAL_TYPE,
|
||||||
|
hp: 10000,
|
||||||
|
id: body.baseAvatarId,
|
||||||
|
satiety: 100,
|
||||||
|
slot,
|
||||||
|
sp: 10000
|
||||||
|
};
|
||||||
|
session.player.setLineup(lineup);
|
||||||
|
session.player.save();
|
||||||
|
|
||||||
session.send(SyncLineupNotify, {
|
session.send("SyncLineupNotify", {
|
||||||
lineup: await session.player.getLineup(index),
|
lineup: lineup,
|
||||||
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,12 +18,12 @@ 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}`);
|
||||||
|
|
||||||
@ -31,34 +31,20 @@ export default async function handle(session: Session, packet: Packet) {
|
|||||||
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;
|
||||||
|
}
|
||||||
|
session.account = account;
|
||||||
|
session.player = player;
|
||||||
|
|
||||||
|
const isTokenValid = player.db.token === body.token;
|
||||||
|
const isBanned = player.db.banned;
|
||||||
|
if (isBanned) dataObj.retcode = 1013;
|
||||||
|
if (!isTokenValid) {
|
||||||
|
retWarn(`Token invalid (${session.ctx.address}:${session.ctx.port})`);
|
||||||
|
dataObj.retcode = 1005;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
session.account = account;
|
|
||||||
session.player = player;
|
|
||||||
dataObj.uid = player.db._id;
|
dataObj.uid = player.db._id;
|
||||||
|
session.send("PlayerGetTokenScRsp", dataObj);
|
||||||
if (player.db.banned) {
|
|
||||||
dataObj.retcode = 1013;
|
|
||||||
dataObj.blackInfo = {
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
import { AvatarType, ExtraLineupType, HeroBasicType, PlayerBasicInfo, PlayerLoginCsReq, PlayerLoginScRsp } from "../../data/proto/StarRail";
|
import { ExtraLineupType, HeroBasicType, PlayerBasicInfo, PlayerLoginCsReq, PlayerLoginScRsp } from "../../data/proto/StarRail";
|
||||||
import Avatar from "../../db/Avatar";
|
import Avatar from "../../db/Avatar";
|
||||||
import Player from "../../db/Player";
|
import Player from "../../db/Player";
|
||||||
import Packet from "../kcp/Packet";
|
import Packet from "../kcp/Packet";
|
||||||
@ -45,8 +45,7 @@ export default async function handle(session: Session, packet: Packet) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!plr.db.lineup) {
|
if (!plr.db.lineup) {
|
||||||
await Avatar.addAvatarToPlayer(plr, 1001);
|
await Avatar.create(plr.db._id, 1001, 0);
|
||||||
//await Avatar.create(plr.db._id, 1001, 0);
|
|
||||||
const baseLineup = {
|
const baseLineup = {
|
||||||
avatarList: [1001],
|
avatarList: [1001],
|
||||||
extraLineupType: ExtraLineupType.LINEUP_NONE,
|
extraLineupType: ExtraLineupType.LINEUP_NONE,
|
||||||
@ -63,8 +62,6 @@ export default async function handle(session: Session, packet: Packet) {
|
|||||||
lineups: {}
|
lineups: {}
|
||||||
}
|
}
|
||||||
for (let i = 0; i <= LINEUPS; i++) {
|
for (let i = 0; i <= LINEUPS; i++) {
|
||||||
let copy = baseLineup;
|
|
||||||
copy.name = `Team ${i}`;
|
|
||||||
plr.db.lineup.lineups[i] = baseLineup;
|
plr.db.lineup.lineups[i] = baseLineup;
|
||||||
}
|
}
|
||||||
plr.save();
|
plr.save();
|
||||||
@ -83,7 +80,7 @@ export default async function handle(session: Session, packet: Packet) {
|
|||||||
plr.save();
|
plr.save();
|
||||||
}
|
}
|
||||||
|
|
||||||
session.send(PlayerLoginScRsp, {
|
session.send("PlayerLoginScRsp", {
|
||||||
basicInfo: plr!.db.basicInfo as PlayerBasicInfo,
|
basicInfo: plr!.db.basicInfo as PlayerBasicInfo,
|
||||||
isNewPlayer: false,
|
isNewPlayer: false,
|
||||||
stamina: 100,
|
stamina: 100,
|
||||||
|
@ -4,5 +4,5 @@ import SRServer from "../kcp/SRServer";
|
|||||||
|
|
||||||
export default async function handle(session: Session, packet: Packet) {
|
export default async function handle(session: Session, packet: Packet) {
|
||||||
// Remove from session list
|
// Remove from session list
|
||||||
SRServer.getInstance().sessions.delete(session.id);
|
SRServer.getInstance().sessions.delete(`${session.ctx.address}:${session.ctx.port}`);
|
||||||
}
|
}
|
@ -1,37 +0,0 @@
|
|||||||
import { PromoteAvatarCsReq, PromoteAvatarScRsp } from "../../data/proto/StarRail";
|
|
||||||
import Avatar from "../../db/Avatar";
|
|
||||||
import { PayItemData } from "../../db/Inventory";
|
|
||||||
import AvatarPromotionExcel from "../../util/excel/AvatarPromotionExcel";
|
|
||||||
import Packet from "../kcp/Packet";
|
|
||||||
import Session from "../kcp/Session";
|
|
||||||
|
|
||||||
export default async function handle(session: Session, packet: Packet) {
|
|
||||||
const body = packet.body as PromoteAvatarCsReq;
|
|
||||||
const inventory = await session.player.getInventory();
|
|
||||||
|
|
||||||
// Get the target avatar.
|
|
||||||
const avatarId = body.baseAvatarId;
|
|
||||||
const avatar = await Avatar.loadAvatarForPlayer(session.player, avatarId);
|
|
||||||
const promotionExcelData = AvatarPromotionExcel.fromId(`${avatarId}:${avatar.db.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(PromoteAvatarScRsp, { retcode: 1 } as PromoteAvatarScRsp);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await inventory.save();
|
|
||||||
|
|
||||||
// Promote the avatar and save.
|
|
||||||
avatar.db.promotion++;
|
|
||||||
await avatar.save();
|
|
||||||
|
|
||||||
// Done. Sync and send response.
|
|
||||||
await session.sync();
|
|
||||||
session.send(PromoteAvatarScRsp, { retcode: 0 } as PromoteAvatarScRsp);
|
|
||||||
}
|
|
@ -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,44 +0,0 @@
|
|||||||
import { RankUpAvatarCsReq, RankUpAvatarScRsp } from "../../data/proto/StarRail";
|
|
||||||
import Avatar from "../../db/Avatar";
|
|
||||||
import { PayItemData } from "../../db/Inventory";
|
|
||||||
import Logger from "../../util/Logger";
|
|
||||||
import Packet from "../kcp/Packet";
|
|
||||||
import Session from "../kcp/Session";
|
|
||||||
const c = new Logger("RankUpAvatarCsReq");
|
|
||||||
|
|
||||||
export default async function handle(session: Session, packet: Packet) {
|
|
||||||
const body = packet.body as RankUpAvatarCsReq;
|
|
||||||
const dataObj: RankUpAvatarScRsp = {
|
|
||||||
retcode: 0
|
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
|
||||||
const inv = await session.player.getInventory();
|
|
||||||
if (!body.costData) return;
|
|
||||||
const list = body.costData.itemList;
|
|
||||||
const arr: Array<PayItemData> = [];
|
|
||||||
|
|
||||||
for (let i = 0; i < list.length; i++) {
|
|
||||||
const item = list[i];
|
|
||||||
if (!item.pileItem) continue;
|
|
||||||
arr.push({
|
|
||||||
count: item.pileItem.itemNum,
|
|
||||||
id: item.pileItem.itemId
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (await inv.payItems(arr)) {
|
|
||||||
const avatar = await Avatar.loadAvatarForPlayer(session.player, body.baseAvatarId);
|
|
||||||
avatar.db.rank = body.rank // gidra moment: ez hack
|
|
||||||
await avatar.save();
|
|
||||||
} else {
|
|
||||||
dataObj.retcode = 1301;
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
c.error(e as Error);
|
|
||||||
dataObj.retcode = 2;
|
|
||||||
} finally {
|
|
||||||
session.send(RankUpAvatarScRsp, dataObj);
|
|
||||||
session.sync();
|
|
||||||
}
|
|
||||||
}
|
|
@ -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,14 +5,13 @@ 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;
|
||||||
}
|
}
|
||||||
for (const entityMotion of body.entityMotionList) {
|
for (const entityMotion of body.entityMotionList) {
|
||||||
const entity = session.player.scene.entities.get(entityMotion.entityId);
|
const entity = session.player.scene.entities.get(entityMotion.entityId);
|
||||||
if (!entity) { //what??
|
if (!entity) { //what??
|
||||||
// session.player.scene.despawnEntity(entityMotion.entityId);
|
session.player.scene.despawnEntity(entityMotion.entityId);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const motion = entityMotion.motion;
|
const motion = entityMotion.motion;
|
||||||
@ -24,11 +23,7 @@ export default async function handle(session: Session, packet: Packet) {
|
|||||||
entity.pos = motion.pos;
|
entity.pos = motion.pos;
|
||||||
if (entity instanceof ActorEntity) {
|
if (entity instanceof ActorEntity) {
|
||||||
entity.mapLayer = entityMotion.mapLayer;
|
entity.mapLayer = entityMotion.mapLayer;
|
||||||
session.player.db.posData.pos = {
|
session.player.db.posData.pos = motion.pos!;
|
||||||
x: motion.pos.x,
|
|
||||||
y: motion.pos.y,
|
|
||||||
z: motion.pos.z
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -39,7 +34,7 @@ export default async function handle(session: Session, packet: Packet) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
session.send(SceneEntityMoveScRsp, {
|
session.send("SceneEntityMoveScRsp", {
|
||||||
retcode: 0,
|
retcode: 0,
|
||||||
downloadData: undefined,
|
downloadData: undefined,
|
||||||
} as SceneEntityMoveScRsp);
|
} as SceneEntityMoveScRsp);
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { SetClientPausedCsReq, SetClientPausedScRsp } from "../../data/proto/StarRail";
|
import { SetClientPausedCsReq } from "../../data/proto/StarRail";
|
||||||
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) {
|
||||||
const body = packet.body as SetClientPausedCsReq;
|
const body = packet.body as SetClientPausedCsReq;
|
||||||
session.send(SetClientPausedScRsp, { retcode: 0, paused: body.paused });
|
session.send("SetClientPausedScRsp", { retcode: 0, paused: body.paused });
|
||||||
}
|
}
|
@ -14,7 +14,7 @@ export default async function handle(session: Session, packet: Packet) {
|
|||||||
session.c.error("Failed to set lineup name", false);
|
session.c.error("Failed to set lineup name", false);
|
||||||
}
|
}
|
||||||
|
|
||||||
session.send(SetLineupNameScRsp, {
|
session.send("SetLineupNameScRsp", {
|
||||||
retcode: 0,
|
retcode: 0,
|
||||||
index: session.player.db.lineup.curIndex,
|
index: session.player.db.lineup.curIndex,
|
||||||
name: body.name
|
name: body.name
|
||||||
|
@ -1,76 +0,0 @@
|
|||||||
import { ChallengeStatus, CurChallenge, ExtraLineupType, Maze, SceneActorInfo, SceneEntityInfo, SceneInfo, SceneNpcInfo, SceneNpcMonsterInfo, ScenePropInfo, StartChallengeCsReq, StartChallengeScRsp } from "../../data/proto/StarRail";
|
|
||||||
import { ActorEntity } from "../../game/entities/Actor";
|
|
||||||
import Packet from "../kcp/Packet";
|
|
||||||
import Session from "../kcp/Session";
|
|
||||||
|
|
||||||
// StartChallengeCsReq { challengeId: 101 }
|
|
||||||
export default async function handle(session: Session, packet: Packet) {
|
|
||||||
const body = packet.body as StartChallengeCsReq;
|
|
||||||
console.log(JSON.stringify(body, undefined, 4));
|
|
||||||
|
|
||||||
// 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, {
|
|
||||||
retcode: 0,
|
|
||||||
curChallenge: {
|
|
||||||
challengeId: body.challengeId,
|
|
||||||
rounds: 1,
|
|
||||||
status: ChallengeStatus.CHALLENGE_DOING,
|
|
||||||
extraLineupType: ExtraLineupType.LINEUP_NONE,
|
|
||||||
killMonsterList: [],
|
|
||||||
deadAvatarNum: 0,
|
|
||||||
} as CurChallenge,
|
|
||||||
maze: {
|
|
||||||
// ? Data from MappingInfoExcelTable
|
|
||||||
id: 30104,
|
|
||||||
mapEntryId: 3000401,
|
|
||||||
floor: {
|
|
||||||
floorId: 20121001,
|
|
||||||
scene: {
|
|
||||||
planeId: 30104,
|
|
||||||
entryId: 3000401,
|
|
||||||
floorId: 30104001,
|
|
||||||
lightenSectionList: [],
|
|
||||||
gameModeType: 4,
|
|
||||||
entityList: [
|
|
||||||
curAvatarEntity.getSceneEntityInfo(),
|
|
||||||
{
|
|
||||||
entityId: 10000,
|
|
||||||
motion: {
|
|
||||||
pos: {
|
|
||||||
x: 74719,
|
|
||||||
y: 2014,
|
|
||||||
z: -94205,
|
|
||||||
},
|
|
||||||
rot: {
|
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
z: 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
groupId: 3,
|
|
||||||
instId: 1,
|
|
||||||
actor: {} as SceneActorInfo,
|
|
||||||
npc: {} as SceneNpcInfo,
|
|
||||||
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);
|
|
||||||
}
|
|
@ -1,21 +0,0 @@
|
|||||||
import { SwapLineupCsReq, SwapLineupScRsp, SyncLineupNotify, SyncLineupReason } 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 SwapLineupCsReq;
|
|
||||||
session.send(SwapLineupScRsp, { retcode: 0 } as SwapLineupScRsp);
|
|
||||||
|
|
||||||
let lineup = await session.player.getLineup();
|
|
||||||
const _copy = lineup.avatarList[body.dstSlot];
|
|
||||||
lineup.avatarList[body.dstSlot] = lineup.avatarList[body.srcSlot];
|
|
||||||
lineup.avatarList[body.srcSlot] = _copy;
|
|
||||||
|
|
||||||
session.player.setLineup(lineup);
|
|
||||||
session.player.save();
|
|
||||||
|
|
||||||
session.send(SyncLineupNotify, {
|
|
||||||
lineup: lineup,
|
|
||||||
reasonList: [SyncLineupReason.SYNC_REASON_NONE]
|
|
||||||
} as SyncLineupNotify);
|
|
||||||
}
|
|
@ -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 ?
|
|
||||||
}
|
|
@ -5,7 +5,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 SyncTimeCsReq;
|
const body = packet.body as SyncTimeCsReq;
|
||||||
|
|
||||||
session.send(SyncTimeScRsp, {
|
session.send("SyncTimeScRsp", {
|
||||||
retcode: 0,
|
retcode: 0,
|
||||||
clientTimeMs: body.clientTimeMs,
|
clientTimeMs: body.clientTimeMs,
|
||||||
serverTimeMs: Math.round(new Date().getTime() / 1000)
|
serverTimeMs: Math.round(new Date().getTime() / 1000)
|
||||||
|
@ -1,15 +0,0 @@
|
|||||||
import { TutorialStatus, UnlockTutorialGuideCsReq, UnlockTutorialGuideScRsp } 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 UnlockTutorialGuideCsReq;
|
|
||||||
|
|
||||||
session.send(UnlockTutorialGuideScRsp, {
|
|
||||||
retcode: 0,
|
|
||||||
tutorialGuide: {
|
|
||||||
id: body.groupId,
|
|
||||||
status: TutorialStatus.TUTORIAL_FINISH
|
|
||||||
}
|
|
||||||
} as UnlockTutorialGuideScRsp);
|
|
||||||
}
|
|
@ -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;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,69 +0,0 @@
|
|||||||
import fs from 'fs';
|
|
||||||
import {resolve} from 'path';
|
|
||||||
import Logger from './Logger';
|
|
||||||
|
|
||||||
const c = new Logger("Banner");
|
|
||||||
|
|
||||||
type Banner = {
|
|
||||||
gachaId: number,
|
|
||||||
detailWebview: string,
|
|
||||||
rateUpItems4: number[],
|
|
||||||
rateUpItems5: number[],
|
|
||||||
costItemId: number
|
|
||||||
}
|
|
||||||
|
|
||||||
function r(...args: string[]) {
|
|
||||||
return resolve(__dirname, ...args);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class Banners {
|
|
||||||
public static config: Banner[];
|
|
||||||
|
|
||||||
public static init() {
|
|
||||||
Banners.readConfig();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static readConfig() {
|
|
||||||
let config: Banner[];
|
|
||||||
|
|
||||||
const defaultConfig: Banner[] = [];
|
|
||||||
|
|
||||||
// TODO: figure where is GachaBasicInfoConfigExcelTable. Temporary hardcode
|
|
||||||
const bannersID = [1001, 2001, 2002, 3001, 3002, 4001]
|
|
||||||
|
|
||||||
for (let i = 0; i < bannersID.length; i++) {
|
|
||||||
defaultConfig.push({
|
|
||||||
gachaId: bannersID[i],
|
|
||||||
detailWebview: "",
|
|
||||||
rateUpItems4: [
|
|
||||||
1001, 1103
|
|
||||||
],
|
|
||||||
rateUpItems5: [
|
|
||||||
1102
|
|
||||||
],
|
|
||||||
costItemId: 101 // Star Rail Pass
|
|
||||||
} as Banner)
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
config = JSON.parse(fs.readFileSync(r('../../banners.json')).toString());
|
|
||||||
|
|
||||||
for (const [index, gachaBanner] of Object.entries(config)) {
|
|
||||||
const missing = Object.keys(defaultConfig[0]).filter(key => !gachaBanner.hasOwnProperty(key));
|
|
||||||
if (missing.length > 0) {
|
|
||||||
c.log(`Missing ${missing.join(', ')}, using default values.`);
|
|
||||||
config[parseInt(index)] = defaultConfig[0];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Banners.updateConfig(config);
|
|
||||||
} catch {
|
|
||||||
c.error("Could not read banners file. Creating one for you...");
|
|
||||||
Banners.updateConfig(defaultConfig);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static updateConfig(config: Banner[]) {
|
|
||||||
this.config = config;
|
|
||||||
fs.writeFileSync(r('../../banners.json'), JSON.stringify(config, null, 2));
|
|
||||||
}
|
|
||||||
}
|
|
@ -7,7 +7,7 @@ const DEFAULT_CONFIG = {
|
|||||||
VERBOSE_LEVEL: 1,
|
VERBOSE_LEVEL: 1,
|
||||||
|
|
||||||
// MongoDB
|
// MongoDB
|
||||||
MONGO_URI: "mongodb://0.0.0.0:27017/crepesr",
|
MONGO_URI: "mongodb://localhost:27017/crepesr",
|
||||||
|
|
||||||
// HTTP
|
// HTTP
|
||||||
HTTP: {
|
HTTP: {
|
||||||
@ -27,14 +27,7 @@ const DEFAULT_CONFIG = {
|
|||||||
SERVER_PORT: 22102,
|
SERVER_PORT: 22102,
|
||||||
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
|
|
||||||
}
|
}
|
||||||
type DefaultConfig = typeof DEFAULT_CONFIG;
|
type DefaultConfig = typeof DEFAULT_CONFIG;
|
||||||
|
|
||||||
@ -87,13 +80,6 @@ export default class Config {
|
|||||||
MAINTENANCE: boolean;
|
MAINTENANCE: boolean;
|
||||||
MAINTENANCE_MSG: string;
|
MAINTENANCE_MSG: string;
|
||||||
} = Config.config.GAMESERVER;
|
} = Config.config.GAMESERVER;
|
||||||
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() { }
|
||||||
}
|
}
|
@ -5,8 +5,6 @@ export enum VerboseLevel {
|
|||||||
NONE = 0, // No logging except for errors
|
NONE = 0, // No logging except for errors
|
||||||
WARNS = 1, // Log warns
|
WARNS = 1, // Log warns
|
||||||
ALL = 2, // Warns and (useless) debug
|
ALL = 2, // Warns and (useless) debug
|
||||||
VERBL = 3, // Warns, debug and verbose
|
|
||||||
VERBH = 4, // Warns, debug, verbose and very verbose (thanks copilot this is so funny)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Color = 'red' | 'green' | 'yellow' | 'blue' | 'magenta' | 'cyan' | 'white' | 'gray' | 'black' | 'italic' | 'bold' | 'underline' | 'strikethrough' | 'inverse' | 'bgRed' | 'bgGreen' | 'bgYellow' | 'bgBlue' | 'bgMagenta' | 'bgCyan' | 'bgWhite' | 'bgBlack' | 'bgGray' | 'bgItalic';
|
type Color = 'red' | 'green' | 'yellow' | 'blue' | 'magenta' | 'cyan' | 'white' | 'gray' | 'black' | 'italic' | 'bold' | 'underline' | 'strikethrough' | 'inverse' | 'bgRed' | 'bgGreen' | 'bgYellow' | 'bgBlue' | 'bgMagenta' | 'bgCyan' | 'bgWhite' | 'bgBlack' | 'bgGray' | 'bgItalic';
|
||||||
@ -52,16 +50,4 @@ export default class Logger {
|
|||||||
console.log(`[${this.getDate().white.bold}] ${`DEBUG<${this.name}>`.bgBlue.bold}`, ...args);
|
console.log(`[${this.getDate().white.bold}] ${`DEBUG<${this.name}>`.bgBlue.bold}`, ...args);
|
||||||
this.trail(new Error().stack!.split('\n').slice(2).join('\n'));
|
this.trail(new Error().stack!.split('\n').slice(2).join('\n'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public verbL(...args: any) {
|
|
||||||
if (Logger.VERBOSE_LEVEL < VerboseLevel.VERBL) return;
|
|
||||||
console.log(`[${this.getDate().white.bold}] ${`VERBL<${this.name}>`.bgCyan.bold}`, ...args);
|
|
||||||
this.trail(new Error().stack!.split('\n').slice(2).join('\n'));
|
|
||||||
}
|
|
||||||
|
|
||||||
public verbH(...args: any) {
|
|
||||||
if (Logger.VERBOSE_LEVEL < VerboseLevel.VERBH) return;
|
|
||||||
console.log(`[${this.getDate().white.bold}] ${`VERBH<${this.name}>`.bgCyan.bold}`, ...args);
|
|
||||||
this.trail(new Error().stack!.split('\n').slice(2).join('\n'));
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -2,27 +2,29 @@
|
|||||||
import * as types from "../data/proto/StarRail";
|
import * as types from "../data/proto/StarRail";
|
||||||
import protobufjs from "protobufjs";
|
import protobufjs from "protobufjs";
|
||||||
import { CmdID, PacketName } from "../server/kcp/Packet"
|
import { CmdID, PacketName } from "../server/kcp/Packet"
|
||||||
import Logger, { VerboseLevel } from "./Logger";
|
import Logger from "./Logger";
|
||||||
const c = new Logger("ProtoFactory");
|
const c = new Logger("ProtoFactory");
|
||||||
|
|
||||||
export class MessageType<T> {
|
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
|
||||||
//fromjson etc...
|
//fromjson etc...
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const messageTypeMap = new Map<PacketName, MessageType<any>>();
|
const messageTypeMap = new Map<PacketName, MessageType<any>>();
|
||||||
const messageTypeMapReversed = new Map<MessageType<any>, PacketName>();
|
const messageTypeMapReversed = new Map<MessageType<any>, PacketName>();
|
||||||
|
|
||||||
|
function send<Class extends MessageType<T>, T>(type: Class, data: T) {
|
||||||
|
console.log(type.encode(data).finish())
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
function isMessageType<T>(type: MessageType<T> | any): type is MessageType<T> {
|
function isMessageType<T>(pet: MessageType<T> | any): pet is MessageType<T> {
|
||||||
return (<MessageType<T>>type).encode !== undefined;
|
return (<MessageType<T>>pet).encode !== undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -54,12 +56,28 @@ export default class ProtoFactory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Logger.VERBOSE_LEVEL > VerboseLevel.ALL) c.log(`Initialized with ${messageTypeMap.size} types`);
|
c.debug(`Initialized with ${messageTypeMap.size} types`);
|
||||||
|
|
||||||
//c.log(this.getName(types.PlayerLoginScRsp))
|
//c.log(this.getName(types.PlayerLoginScRsp))
|
||||||
// return;
|
return;
|
||||||
|
|
||||||
//if you want a partial type
|
//if you want a partial type
|
||||||
|
send(types.PlayerLoginScRsp, types.PlayerLoginScRsp.fromPartial({
|
||||||
|
basicInfo: {
|
||||||
|
exp: 0,
|
||||||
|
level: 1,
|
||||||
|
hcoin: 0,
|
||||||
|
mcoin: 0,
|
||||||
|
nickname: "test",
|
||||||
|
scoin: 0,
|
||||||
|
stamina: 100,
|
||||||
|
worldLevel: 1,
|
||||||
|
},
|
||||||
|
isNewPlayer: true,
|
||||||
|
stamina: 100,
|
||||||
|
curTimezone: 1,
|
||||||
|
serverTimestampMs: Math.round(new Date().getTime() / 1000),
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,20 +0,0 @@
|
|||||||
import _AvatarExpItemConfigExcelTable from "../../data/excel/AvatarExpItemConfigExcelTable.json";
|
|
||||||
type AvatarExpItemConfigExcelTableEntry = typeof _AvatarExpItemConfigExcelTable[keyof typeof _AvatarExpItemConfigExcelTable]
|
|
||||||
const AvatarExpItemConfigExcelTable = _AvatarExpItemConfigExcelTable as { [key: string]: AvatarExpItemConfigExcelTableEntry };
|
|
||||||
|
|
||||||
export default class AvatarExpItemExcel {
|
|
||||||
private constructor() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public static all() : AvatarExpItemConfigExcelTableEntry[] {
|
|
||||||
return Object.values(AvatarExpItemConfigExcelTable);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static fromId(id: number) : AvatarExpItemConfigExcelTableEntry {
|
|
||||||
return AvatarExpItemConfigExcelTable[id];
|
|
||||||
}
|
|
||||||
|
|
||||||
public static fromIds(ids: number[]): AvatarExpItemConfigExcelTableEntry[] {
|
|
||||||
return ids.map(id => AvatarExpItemExcel.fromId(id));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,20 +0,0 @@
|
|||||||
import _AvatarPromotionExcelTable from "../../data/excel/AvatarPromotionExcelTable.json";
|
|
||||||
type AvatarPromotionExcelTableEntry = typeof _AvatarPromotionExcelTable[keyof typeof _AvatarPromotionExcelTable]
|
|
||||||
const AvatarPromotionExcelTable = _AvatarPromotionExcelTable as { [key: string]: AvatarPromotionExcelTableEntry };
|
|
||||||
|
|
||||||
export default class AvatarPromotionExcel {
|
|
||||||
private constructor() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public static all() : AvatarPromotionExcelTableEntry[] {
|
|
||||||
return Object.values(AvatarPromotionExcelTable);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static fromId(id: string) : AvatarPromotionExcelTableEntry {
|
|
||||||
return AvatarPromotionExcelTable[id];
|
|
||||||
}
|
|
||||||
|
|
||||||
public static fromIds(ids: string[]): AvatarPromotionExcelTableEntry[] {
|
|
||||||
return ids.map(id => AvatarPromotionExcel.fromId(id));
|
|
||||||
}
|
|
||||||
}
|
|
33
src/util/excel/DailyMissionDataExcel.ts
Normal file
33
src/util/excel/DailyMissionDataExcel.ts
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import DailyMissionDataExcelTable from "../../data/excel/DailyMissionDataExcelTable.json";
|
||||||
|
import _DailyMissionRewardExcelTable from "../../data/excel/DailyMissionRewardExcelTable.json";
|
||||||
|
const DailyMissionRewardExcelTable: { [key: string]: DailyMissionRewardExcelTableEntry } = _DailyMissionRewardExcelTable;
|
||||||
|
|
||||||
|
interface DailyMissionRewardExcelTableEntry {
|
||||||
|
WorldLevel: number;
|
||||||
|
FinishCount: number;
|
||||||
|
RewardID: number;
|
||||||
|
ExtraRewardID: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class DailyMissionDataExcel {
|
||||||
|
private constructor() { }
|
||||||
|
|
||||||
|
public static fromId(id: number) {
|
||||||
|
return Object.values(DailyMissionDataExcelTable).find(x => x.ID === id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static random(amount: number = 5) {
|
||||||
|
const ids = Object.keys(DailyMissionDataExcelTable);
|
||||||
|
const randomIds = [];
|
||||||
|
for (let i = 0; i < amount; i++) {
|
||||||
|
const id = ids[Math.floor(Math.random() * ids.length)];
|
||||||
|
if (randomIds.indexOf(id) === -1) randomIds.push(id);
|
||||||
|
}
|
||||||
|
return randomIds.map(x => DailyMissionDataExcel.fromId(Number(x)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static getReward(finished: number, wl: number = 1) {
|
||||||
|
const key = `${wl}:${finished}`;
|
||||||
|
return DailyMissionRewardExcelTable[key];
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user