Implementing entities. (#15)

* player: allowing player to send packet outside of packet handlers
entity: simple entity implementation, spawning doesnt work currently

* fix: conflict

* fix: faulty request data

* fix: oopsie

* again...

* requested: file name & fixes for latest commit

* fix: memetrolls on drugs

* [CodeFactor] Apply fixes

[ci skip] [skip ci]

* Minor changes

* Formatting

* Rename actor.ts to Actor.ts

* Rename entity.ts to Entity.ts

* Rename monster.ts to Monster.ts

* Rename npc.ts to NPC.ts

* Rename prop.ts to Prop.ts

* Rename scene.ts to Scene.ts

Co-authored-by: labalityowo <56186498+labalityowo@users.noreply.github.com>
Co-authored-by: codefactor-io <support@codefactor.io>
Co-authored-by: memetrollsXD <memetrollsxd@gmail.com>
This commit is contained in:
timing1337 2022-08-02 22:06:14 +07:00 committed by GitHub
parent 0098da757c
commit 83d81487df
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 252 additions and 22 deletions

View File

@ -10,7 +10,7 @@ export default class Avatar {
}
public static async create(uid: UID, baseAvatarId: number = 1001): Promise<Avatar> {
public static async create(uid: UID, baseAvatarId: number = 1001, slot: number = -1): Promise<Avatar> {
const db = Database.getInstance();
// Check if already exists
const existing = await Avatar.fromUID(uid, baseAvatarId);
@ -29,7 +29,7 @@ export default class Avatar {
hp: 10000,
id: baseAvatarId,
satiety: 100,
slot: -1,
slot: slot,
sp: 10000
});
db.set("avatars", avatar);

View File

@ -1,8 +1,10 @@
import Session from "../server/kcp/Session";
import { ExtraLineupType, HeroBasicType, LineupInfo, Vector } from "../data/proto/StarRail";
import Logger from "../util/Logger";
import Account from "./Account";
import Avatar from "./Avatar";
import Database from "./Database";
import { Scene } from "../game/Scene";
const c = new Logger("Player");
export interface LineupI {
@ -40,30 +42,33 @@ interface PlayerI {
posData: {
floorID: number;
planeID: number;
pos?: Vector;
pos: Vector;
}
}
export default class Player {
public readonly uid: number;
private constructor(public db: PlayerI) {
public readonly uid: number
public readonly scene: Scene;
private constructor(readonly session: Session, public db: PlayerI) {
this.uid = db._id;
this.scene = new Scene(this);
}
public static async fromUID(uid: number | string): Promise<Player | undefined> {
public static async fromUID(session: Session, uid: number | string): Promise<Player | undefined> {
if (typeof uid == "string") uid = Number(uid);
const db = Database.getInstance();
const player = await db.get("players", { _id: uid }) as unknown as PlayerI;
if (!player) return Player.create(uid);
return new Player(player);
if (!player) return Player.create(session, uid);
return new Player(session, player);
}
public static async fromToken(token: string): Promise<Player | undefined> {
public static async fromToken(session: Session, token: string): Promise<Player | undefined> {
const db = Database.getInstance();
const plr = await db.get("players", { token }) as unknown as PlayerI;
if (!plr) return Player.fromUID((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(plr);
return new Player(session, plr);
}
public async getLineup(lineupIndex?: number): Promise<LineupInfo> {
@ -89,7 +94,7 @@ export default class Player {
this.db.lineup.curIndex = curIndex;
}
public static async create(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);
const acc = await Account.fromUID(uid);
if (!acc) {
@ -119,7 +124,12 @@ export default class Player {
},
posData: {
floorID: 10001001,
planeID: 10001
planeID: 10001,
pos: {
x: 0,
y: 439,
z: -45507
}
},
banned: false
} as PlayerI
@ -141,13 +151,16 @@ export default class Player {
lineups: {}
}
for (let i = 0; i <= LINEUPS; i++) {
let copy = baseLineup;
const copy = baseLineup;
copy.index = slot++;
dataObj.lineup.lineups[i] = copy;
}
await Avatar.create(uid, 1001, 0);
await db.set("players", dataObj);
return new Player(dataObj);
return new Player(session, dataObj);
}
public async save() {

30
src/game/Scene.ts Normal file
View File

@ -0,0 +1,30 @@
import { SceneEntityDisappearScNotify, SceneEntityUpdateScNotify } from "../data/proto/StarRail";
import Player from "../db/Player";
import { Entity } from "./entities/Entity";
export class Scene {
public entryId!: number;
public entities: Map<number, Entity> = new Map<number, Entity>();
constructor(public readonly player: Player) {
}
public spawnEntity(entity: Entity, silent: boolean = false) {
this.entities.set(entity.entityId, entity);
if (!silent) {
this.player.session.send("SceneEntityUpdateScNotify", {
entityList: [entity.getSceneEntityInfo()]
} as SceneEntityUpdateScNotify);
}
}
public despawnEntity(entityId: number, silent: boolean = false) {
this.entities.delete(entityId);
if (!silent) {
this.player.session.send("SceneEntityDisappearScNotify", {
entityIdList: [entityId]
} as SceneEntityDisappearScNotify);
}
}
}

View File

@ -0,0 +1,28 @@
import { AvatarType, EntityType, MotionInfo, SceneActorInfo, SceneEntityInfo, Vector } from "../../data/proto/StarRail";
import { Scene } from "../Scene";
import { Entity } from "./Entity";
export class ActorEntity extends Entity
{
public mapLayer: number = 0;
constructor(readonly scene: Scene, public readonly avatarId: number, public pos: Vector, public rot?: Vector){
super(scene, pos, rot);
}
public getSceneEntityInfo(): SceneEntityInfo {
const sceneEntityInfo = super.getSceneEntityInfo();
sceneEntityInfo.actor = {
avatarType: AvatarType.AVATAR_FORMAL_TYPE,
baseAvatarId: this.avatarId,
uid: this.scene.player.db._id,
mapLayer: 0 //?
};
return sceneEntityInfo;
}
public getEntityType(): EntityType {
return EntityType.ENTITY_AVATAR;
}
}

View File

@ -0,0 +1,33 @@
import { EntityType, MotionInfo, SceneEntityInfo, Vector } from "../../data/proto/StarRail";
import { Scene } from "../Scene";
export abstract class Entity{
static nextEntityId: number = 0;
readonly entityId: number;
constructor(readonly scene: Scene, public pos: Vector, public rot?: Vector){
this.entityId = Entity.nextEntityId++;
}
public abstract getEntityType(): EntityType;
public getSceneEntityInfo(): SceneEntityInfo{
return {
entityId: this.entityId,
groupId: 1,
instId: 1,
motion: {
pos: this.pos,
rot: this.rot ?? {
x: 0,
y: 0,
z: 0,
},
} as MotionInfo,
actor: {},
npc: {},
npcMonster: {},
prop: {}
} as SceneEntityInfo;
}
}

View File

@ -0,0 +1,29 @@
import { AvatarType, EntityType, MotionInfo, SceneActorInfo, SceneEntityInfo, SceneNpcMonsterInfo, Vector } from "../../data/proto/StarRail";
import { Scene } from "../Scene";
import { Entity } from "./Entity";
export class MonsterEntity extends Entity
{
public mapLayer: number = 0;
constructor(readonly scene: Scene, public readonly monsterId: number, public pos: Vector, public rot?: Vector){
super(scene, pos, rot);
}
public getSceneEntityInfo(): SceneEntityInfo {
const sceneEntityInfo = super.getSceneEntityInfo();
sceneEntityInfo.npcMonster = {
eventId: 1,
isGenMonster: true,
isSetWorldLevel: false,
worldLevel: 1,//hardcoded
monsterId: this.monsterId,
} as SceneNpcMonsterInfo;
return sceneEntityInfo;
}
public getEntityType(): EntityType {
return EntityType.ENTITY_MONSTER;
}
}

26
src/game/entities/NPC.ts Normal file
View File

@ -0,0 +1,26 @@
import { EntityType, NpcExtraInfo, PropExtraInfo, SceneEntityInfo, SceneNpcInfo, SceneNpcMonsterInfo, ScenePropInfo, Vector } from "../../data/proto/StarRail";
import { Scene } from "../Scene";
import { Entity } from "./Entity";
export class NpcEntity extends Entity
{
public mapLayer: number = 0;
constructor(readonly scene: Scene, public readonly npcId: number, public pos: Vector, public rot?: Vector){
super(scene, pos, rot);
}
public getSceneEntityInfo(): SceneEntityInfo {
const sceneEntityInfo = super.getSceneEntityInfo();
sceneEntityInfo.npc = {
npcId: this.npcId,
extraInfo: {} as NpcExtraInfo
} as SceneNpcInfo;
return sceneEntityInfo;
}
public getEntityType(): EntityType {
return EntityType.ENTITY_PROP;
}
}

29
src/game/entities/Prop.ts Normal file
View File

@ -0,0 +1,29 @@
import { EntityType, PropExtraInfo, SceneEntityInfo, SceneNpcMonsterInfo, ScenePropInfo, Vector } from "../../data/proto/StarRail";
import { Scene } from "../Scene";
import { Entity } from "./Entity";
export class PropEntity extends Entity
{
public mapLayer: number = 0;
constructor(readonly scene: Scene, public readonly propId: number, public pos: Vector, public rot?: Vector){
super(scene, pos, rot);
}
public getSceneEntityInfo(): SceneEntityInfo {
const sceneEntityInfo = super.getSceneEntityInfo();
sceneEntityInfo.prop = {
propId: this.propId,
createTimeMs: Date.now(),
extraInfo: {} as PropExtraInfo,
lifeTimeMs: Date.now() + 1000 * 60 * 60,
propState: 1,
} as ScenePropInfo;
return sceneEntityInfo;
}
public getEntityType(): EntityType {
return EntityType.ENTITY_PROP;
}
}

View File

@ -1,15 +1,21 @@
import { GetCurSceneInfoScRsp } from "../../data/proto/StarRail";
import { GetCurSceneInfoScRsp, Vector } from "../../data/proto/StarRail";
import { ActorEntity } from "../../game/entities/Actor";
import Packet from "../kcp/Packet";
import Session from "../kcp/Session";
export default async function handle(session: Session, packet: Packet) {
const posData = session.player.db.posData;
const _lineup = session.player.db.lineup;
const lineup = _lineup.lineups[_lineup.curIndex];
const curAvatarEntity = new ActorEntity(session.player.scene, lineup.avatarList[0], posData.pos);
session.send("GetCurSceneInfoScRsp", {
retcode: 0,
scene: {
planeId: posData.planeID,
floorId: posData.floorID,
entityList: [],
entityList: [
curAvatarEntity
],
entityBuffList: [],
entryId: 10001,
envBuffList: [],
@ -17,4 +23,6 @@ export default async function handle(session: Session, packet: Packet) {
lightenSectionList: []
},
} as unknown as GetCurSceneInfoScRsp);
session.player.scene.spawnEntity(curAvatarEntity, true);
session.player.scene.entryId = 10001;
}

View File

@ -27,7 +27,7 @@ export default async function handle(session: Session, packet: Packet) {
const account = await Account.fromToken(body.token || "");
if (!account) retWarn(`Account not found with token ${body.token}`);
const player = await Player.fromToken(account?.token || "");
const player = await Player.fromToken(session, account?.token || "");
if (!player) retWarn(`Player not found with accountToken ${account?.token}`);
if (!player || !account) {
dataObj.retcode = 6;

View File

@ -22,7 +22,7 @@ import Session from "../kcp/Session";
export default async function handle(session: Session, packet: Packet) {
const body = packet.body as PlayerLoginCsReq;
const plr = await Player.fromUID(session.player.db._id);
const plr = await Player.fromUID(session, session.player.db._id);
if (!plr) return;
if (!plr.db.heroBasicType) {
@ -45,7 +45,7 @@ export default async function handle(session: Session, packet: Packet) {
}
if (!plr.db.lineup) {
Avatar.create(plr.db._id);
await Avatar.create(plr.db._id, 1001, 0);
const baseLineup = {
avatarList: [1001],
extraLineupType: ExtraLineupType.LINEUP_NONE,
@ -63,7 +63,7 @@ export default async function handle(session: Session, packet: Packet) {
lineups: {}
}
for (let i = 0; i <= LINEUPS; i++) {
let copy = baseLineup;
const copy = baseLineup;
copy.index = slot++;
plr.db.lineup.lineups[i] = copy;
}
@ -73,7 +73,12 @@ export default async function handle(session: Session, packet: Packet) {
if (!plr.db.posData) {
plr.db.posData = {
floorID: 10001001,
planeID: 10001
planeID: 10001,
pos: {
x: 0,
y: 439,
z: -45507
}
}
plr.save();
}

View File

@ -1,9 +1,38 @@
import { SceneEntityMoveCsReq, SceneEntityMoveScRsp } from "../../data/proto/StarRail";
import { ActorEntity } from "../../game/entities/Actor";
import Packet from "../kcp/Packet";
import Session from "../kcp/Session";
export default async function handle(session: Session, packet: Packet) {
const body = packet.body as SceneEntityMoveCsReq;
if (session.player.scene.entryId !== body.entryId) {
return;
}
for (const entityMotion of body.entityMotionList) {
const entity = session.player.scene.entities.get(entityMotion.entityId);
if (!entity) { //what??
session.player.scene.despawnEntity(entityMotion.entityId);
continue;
}
const motion = entityMotion.motion;
if (!motion) {
continue;
}
if (motion.pos && (Object.keys(motion.pos!).length > 0)) { //preventing motion sending empty pos causing the pos to be reset
entity.pos = motion.pos;
if (entity instanceof ActorEntity) {
entity.mapLayer = entityMotion.mapLayer;
session.player.db.posData.pos = motion.pos!;
}
}
entity.rot = (Object.keys(motion.rot!).length > 0) ? motion.rot : {
x: 0,
y: 0,
z: 0,
}
}
session.send("SceneEntityMoveScRsp", {
retcode: 0,