mirror of
https://github.com/Terncode/pixel.horse.git
synced 2026-09-24 21:55:52 +02:00
Merge branch 'archive' into archive-update-v0.55.2
This commit is contained in:
@@ -342,6 +342,13 @@ Read more about it [here](https://stackoverflow.com/questions/1139762/ignore-fil
|
|||||||
|
|
||||||
## Changelog
|
## Changelog
|
||||||
|
|
||||||
|
#### Pony.Town v0.55.2
|
||||||
|
- Optimized server performance
|
||||||
|
- Fixed various animation issues
|
||||||
|
- Fixed issues with kiss animation while turning head
|
||||||
|
- Added additional server performance stats to the admin panel
|
||||||
|
- Changed the button on admin panel that removes old events from 10min to 1day
|
||||||
|
|
||||||
### Pony.Town v0.55.0
|
### Pony.Town v0.55.0
|
||||||
- Added Kiss action
|
- Added Kiss action
|
||||||
- Added being able to extinguish torches by sneezing
|
- Added being able to extinguish torches by sneezing
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "pony-town",
|
"name": "pony-town",
|
||||||
"version": "0.55.0",
|
"version": "0.55.2",
|
||||||
"description": "A game of ponies building a town",
|
"description": "A game of ponies building a town",
|
||||||
"main": "pony-town.js",
|
"main": "pony-town.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ const ICON_SIZE = 16;
|
|||||||
const headX = -26;
|
const headX = -26;
|
||||||
const headY = -30;
|
const headY = -30;
|
||||||
const headlessBoopFrame = { ...cloneDeep(boop.frames[7]), head: 0 };
|
const headlessBoopFrame = { ...cloneDeep(boop.frames[7]), head: 0 };
|
||||||
const headlessBoop: BodyAnimation = { name: '', loop: false, fps: 1, frames: [headlessBoopFrame] };
|
const headlessBoop: BodyAnimation = { name: '', loop: false, fps: 1, frames: [headlessBoopFrame], disableHeadTurnFrames: 0 };
|
||||||
|
|
||||||
function createPony(coatColor: string, wings = false, horn = false) {
|
function createPony(coatColor: string, wings = false, horn = false) {
|
||||||
const info = createDefaultPony();
|
const info = createDefaultPony();
|
||||||
|
|||||||
@@ -139,7 +139,21 @@ export function boopAction(game: PonyTownGame) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function turnHeadAction(game: PonyTownGame) {
|
export function turnHeadAction(game: PonyTownGame) {
|
||||||
if (game.player && game.send(server => server.action(Action.TurnHead))) {
|
if (!game.player) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (game.player.animator.state) {
|
||||||
|
const disabledFrames = game.player.animator.state.animation.disableHeadTurnFrames;
|
||||||
|
if (disabledFrames > 0) {
|
||||||
|
const disabledTime = disabledFrames / game.player.animator.state.animation.fps;
|
||||||
|
if (disabledTime >= game.player.animator.time) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (game.send(server => server.action(Action.TurnHead))) {
|
||||||
game.player.state = game.player.state ^ EntityState.HeadTurned;
|
game.player.state = game.player.state ^ EntityState.HeadTurned;
|
||||||
game.headTurnedOverride = hasFlag(game.player.state, EntityState.HeadTurned);
|
game.headTurnedOverride = hasFlag(game.player.state, EntityState.HeadTurned);
|
||||||
game.onActionsUpdate.next();
|
game.onActionsUpdate.next();
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ export function createBodyFrame([
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function createBodyAnimation(
|
export function createBodyAnimation(
|
||||||
name: string, fps: number, loop: boolean, frames: number[][], shadowOffsets?: number[][]
|
name: string, fps: number, loop: boolean, frames: number[][], shadowOffsets?: number[][], disableHeadTurnFrames?: number
|
||||||
): Readonly<BodyAnimation> {
|
): Readonly<BodyAnimation> {
|
||||||
if (shadowOffsets && shadowOffsets.length !== frames.length) {
|
if (shadowOffsets && shadowOffsets.length !== frames.length) {
|
||||||
throw new Error(`Incorrect frame count for shadowOffsets for ${name}, animation frames ${frames.length}, shadow frames ${shadowOffsets.length}`);
|
throw new Error(`Incorrect frame count for shadowOffsets for ${name}, animation frames ${frames.length}, shadow frames ${shadowOffsets.length}`);
|
||||||
@@ -28,7 +28,7 @@ export function createBodyAnimation(
|
|||||||
|
|
||||||
const shadow = shadowOffsets && shadowOffsets.map<BodyShadow>(([frame, offset]) => ({ frame, offset }));
|
const shadow = shadowOffsets && shadowOffsets.map<BodyShadow>(([frame, offset]) => ({ frame, offset }));
|
||||||
|
|
||||||
return { name, loop, fps, frames: frames.map(createBodyFrame), shadow };
|
return { name, loop, fps, frames: frames.map(createBodyFrame), shadow, disableHeadTurnFrames: disableHeadTurnFrames || 0 };
|
||||||
}
|
}
|
||||||
|
|
||||||
export const stand = createBodyAnimation('stand', 24, true, [
|
export const stand = createBodyAnimation('stand', 24, true, [
|
||||||
@@ -468,7 +468,7 @@ export const kissBody = createBodyAnimation('kiss-body', 24, false, [
|
|||||||
...repeat(2, [5, 1, 1, 0, 26, 26, 19, 19, -10, -6, 1, 0, 1, 1, 1, 1, 1, -1, 1, -1]),
|
...repeat(2, [5, 1, 1, 0, 26, 26, 19, 19, -10, -6, 1, 0, 1, 1, 1, 1, 1, -1, 1, -1]),
|
||||||
[5, 1, 1, 0, 28, 28, 18, 18, -9, -5, 1, 0, 1, 1, 1, 1, 1, -1, 1, -1],
|
[5, 1, 1, 0, 28, 28, 18, 18, -9, -5, 1, 0, 1, 1, 1, 1, 1, -1, 1, -1],
|
||||||
...repeat(2, [2, 1, 1, 0, 1, 1, 1, 1, -1, 0, -1, 1, 1, 0, 1, 0, 1, 0, 1])
|
...repeat(2, [2, 1, 1, 0, 1, 1, 1, 1, -1, 0, -1, 1, 1, 0, 1, 0, 1, 0, 1])
|
||||||
]);
|
], undefined, 12);
|
||||||
|
|
||||||
export const kissLiftHoofBody = createBodyAnimation('kiss-lift-hoof-body', 24, false, [
|
export const kissLiftHoofBody = createBodyAnimation('kiss-lift-hoof-body', 24, false, [
|
||||||
...repeat(3, [2, 1, 0, 0, 1, 1, 1, 1, -1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1]),
|
...repeat(3, [2, 1, 0, 0, 1, 1, 1, 1, -1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1]),
|
||||||
@@ -479,7 +479,7 @@ export const kissLiftHoofBody = createBodyAnimation('kiss-lift-hoof-body', 24, f
|
|||||||
...repeat(2, [5, 1, 1, 0, 8, 26, 19, 19, -10, -6, 1, 0, 1, 2, 1, 1, 1, -1, 1, -1]),
|
...repeat(2, [5, 1, 1, 0, 8, 26, 19, 19, -10, -6, 1, 0, 1, 2, 1, 1, 1, -1, 1, -1]),
|
||||||
[5, 1, 1, 0, 28, 28, 18, 18, -9, -5, 1, 0, 1, 1, 1, 1, 1, -1, 1, -1],
|
[5, 1, 1, 0, 28, 28, 18, 18, -9, -5, 1, 0, 1, 1, 1, 1, 1, -1, 1, -1],
|
||||||
...repeat(2, [2, 1, 1, 0, 1, 1, 1, 1, -1, 0, -1, 1, 1, 0, 1, 0, 1, 0, 1])
|
...repeat(2, [2, 1, 1, 0, 1, 1, 1, 1, -1, 0, -1, 1, 1, 0, 1, 0, 1, 0, 1])
|
||||||
]);
|
], undefined, 12);
|
||||||
|
|
||||||
export const kissFlyBody = createBodyAnimation('kiss-fly-body', 16, false, [
|
export const kissFlyBody = createBodyAnimation('kiss-fly-body', 16, false, [
|
||||||
[1, 1, 3, 0, 8, 10, 6, 5, 0, -16],
|
[1, 1, 3, 0, 8, 10, 6, 5, 0, -16],
|
||||||
@@ -514,7 +514,7 @@ export const kissFlyBody = createBodyAnimation('kiss-fly-body', 16, false, [
|
|||||||
[1, 1, 12, 0, 9, 10, 4, 4, 0, -17, -1, 0, 0, 0, 0, -1],
|
[1, 1, 12, 0, 9, 10, 4, 4, 0, -17, -1, 0, 0, 0, 0, -1],
|
||||||
[1, 1, 3, 0, 8, 10, 5, 4, 0, -16, 0, 0, 0, 0, 0, -1],
|
[1, 1, 3, 0, 8, 10, 5, 4, 0, -16, 0, 0, 0, 0, 0, -1],
|
||||||
[1, 1, 4, 0, 8, 10, 6, 5, 0, -15]
|
[1, 1, 4, 0, 8, 10, 6, 5, 0, -15]
|
||||||
]);
|
], undefined, 12);
|
||||||
|
|
||||||
export const kissFlyBugBody = createBodyAnimation('kiss-fly-bug-body', 24, false, [
|
export const kissFlyBugBody = createBodyAnimation('kiss-fly-bug-body', 24, false, [
|
||||||
[1, 1, 3, 0, 8, 10, 6, 5, 0, -16],
|
[1, 1, 3, 0, 8, 10, 6, 5, 0, -16],
|
||||||
@@ -557,7 +557,7 @@ export const kissFlyBugBody = createBodyAnimation('kiss-fly-bug-body', 24, false
|
|||||||
[1, 1, 4, 0, 9, 10, 4, 4, 0, -17, -1, 0, 0, 0, 0, -1],
|
[1, 1, 4, 0, 9, 10, 4, 4, 0, -17, -1, 0, 0, 0, 0, -1],
|
||||||
[1, 1, 5, 0, 8, 10, 5, 4, 0, -17, 0, 0, 0, 0, 0, -1],
|
[1, 1, 5, 0, 8, 10, 5, 4, 0, -17, 0, 0, 0, 0, 0, -1],
|
||||||
[1, 1, 4, 0, 8, 10, 6, 5, 0, -17]
|
[1, 1, 4, 0, 8, 10, 6, 5, 0, -17]
|
||||||
]);
|
], undefined, 12);
|
||||||
|
|
||||||
export const kissLieBody = createBodyAnimation('kiss-lie-body', 24, false, [
|
export const kissLieBody = createBodyAnimation('kiss-lie-body', 24, false, [
|
||||||
...repeat(3, [14, 1, 0, 2, 38, 38, 26, 26]),
|
...repeat(3, [14, 1, 0, 2, 38, 38, 26, 26]),
|
||||||
@@ -569,7 +569,7 @@ export const kissLieBody = createBodyAnimation('kiss-lie-body', 24, false, [
|
|||||||
[13, 1, 0, 2, 38, 38, 26, 26, 0, 0, -1],
|
[13, 1, 0, 2, 38, 38, 26, 26, 0, 0, -1],
|
||||||
...repeat(2, [14, 1, 0, 2, 38, 38, 26, 26, 0, 0, -1]),
|
...repeat(2, [14, 1, 0, 2, 38, 38, 26, 26, 0, 0, -1]),
|
||||||
...repeat(2, [14, 1, 0, 2, 38, 38, 26, 26])
|
...repeat(2, [14, 1, 0, 2, 38, 38, 26, 26])
|
||||||
], [...repeat(72, [3, 3])]);
|
], [...repeat(72, [3, 3])], 70);
|
||||||
|
|
||||||
export const kissSitBody = createBodyAnimation('kiss-sit-body', 24, false, [
|
export const kissSitBody = createBodyAnimation('kiss-sit-body', 24, false, [
|
||||||
...repeat(3, [8, 1, 2, 2, 34, 34, 25, 25]),
|
...repeat(3, [8, 1, 2, 2, 34, 34, 25, 25]),
|
||||||
@@ -579,7 +579,7 @@ export const kissSitBody = createBodyAnimation('kiss-sit-body', 24, false, [
|
|||||||
...repeat(60, [9, 1, 2, 2, 39, 39, 26, 26, -1, -1, -2, 0, 0, 1, 0, 1, 1, 1, 1, 1]),
|
...repeat(60, [9, 1, 2, 2, 39, 39, 26, 26, -1, -1, -2, 0, 0, 1, 0, 1, 1, 1, 1, 1]),
|
||||||
...repeat(2, [9, 1, 2, 2, 34, 34, 26, 26, 0, -1, -2, 0, 0, 1, 0, 1, 0, 1, 0, 1]),
|
...repeat(2, [9, 1, 2, 2, 34, 34, 26, 26, 0, -1, -2, 0, 0, 1, 0, 1, 0, 1, 0, 1]),
|
||||||
...repeat(2, [9, 1, 2, 2, 34, 34, 26, 26, 0, 0, -1])
|
...repeat(2, [9, 1, 2, 2, 34, 34, 26, 26, 0, 0, -1])
|
||||||
], [...repeat(72, [0, 6])]);
|
], [...repeat(72, [0, 6])], 68);
|
||||||
|
|
||||||
export const kissSwimBody = createBodyAnimation('kiss-swim-body', 8, false, [
|
export const kissSwimBody = createBodyAnimation('kiss-swim-body', 8, false, [
|
||||||
...repeat(2, [1, 1, 0, 0, 8, 10, 6, 5, 0, 14]),
|
...repeat(2, [1, 1, 0, 0, 8, 10, 6, 5, 0, 14]),
|
||||||
@@ -596,7 +596,7 @@ export const kissSwimBody = createBodyAnimation('kiss-swim-body', 8, false, [
|
|||||||
...repeat(2, [1, 1, 0, 0, 9, 10, 4, 4, -1, 12, -1, 0, 0, 0, 0, -1]),
|
...repeat(2, [1, 1, 0, 0, 9, 10, 4, 4, -1, 12, -1, 0, 0, 0, 0, -1]),
|
||||||
[1, 1, 0, 0, 8, 10, 4, 4, 0, 13, -1, 0, 0, 0, 0, -1],
|
[1, 1, 0, 0, 8, 10, 4, 4, 0, 13, -1, 0, 0, 0, 0, -1],
|
||||||
[1, 1, 0, 0, 8, 10, 5, 5, 0, 13]
|
[1, 1, 0, 0, 8, 10, 5, 5, 0, 13]
|
||||||
]);
|
], undefined, 12);
|
||||||
|
|
||||||
export const kissToTrot = createBodyAnimation('kiss-to-trot-body', 24, false, [
|
export const kissToTrot = createBodyAnimation('kiss-to-trot-body', 24, false, [
|
||||||
[2, 1, 0, 0, 8, 4, 19, 4, -1, 0, -1, 1, 0, 0, 0, 0, 1, -1],
|
[2, 1, 0, 0, 8, 4, 19, 4, -1, 0, -1, 1, 0, 0, 0, 0, 1, -1],
|
||||||
@@ -626,6 +626,7 @@ export function mergeAnimations(name: string, fps: number, loop: boolean, animat
|
|||||||
name,
|
name,
|
||||||
fps,
|
fps,
|
||||||
loop,
|
loop,
|
||||||
|
disableHeadTurnFrames: Math.min(...animations.map(a => a.disableHeadTurnFrames)),
|
||||||
frames: flatten(animations.map(a => a.frames)),
|
frames: flatten(animations.map(a => a.frames)),
|
||||||
shadow: flatten(animations.map(a => a.shadow || a.frames.map(() => ({ frame: 0, offset: 0 })))),
|
shadow: flatten(animations.map(a => a.shadow || a.frames.map(() => ({ frame: 0, offset: 0 })))),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -127,20 +127,19 @@ transition(standing, kissing, { exitAfter: 0 });
|
|||||||
transition(kissingHoof, standing);
|
transition(kissingHoof, standing);
|
||||||
transition(standing, kissingHoof, { exitAfter: 0 });
|
transition(standing, kissingHoof, { exitAfter: 0 });
|
||||||
transition(kissingFlying, hovering, { enterTime: 1.1 / 10 });
|
transition(kissingFlying, hovering, { enterTime: 1.1 / 10 });
|
||||||
transition(hovering, kissingFlying, { exitAfter: 0 });
|
transition(hovering, kissingFlying, { exitAfter: 0, onlyDirectTo: kissingFlying });
|
||||||
transition(kissingLying, lying);
|
|
||||||
transition(lying, kissingLying, { exitAfter: 0 });
|
|
||||||
transition(kissingSitting, sitting);
|
|
||||||
transition(sitting, kissingSitting, { exitAfter: 0 });
|
|
||||||
|
|
||||||
transition(kissing, kissingToTrotting, { exitAfter: 0, onlyDirectTo: trotting });
|
transition(kissing, kissingToTrotting, { exitAfter: 0, onlyDirectTo: trotting });
|
||||||
transition(kissingToTrotting, trotting);
|
transition(kissingToTrotting, trotting);
|
||||||
|
transition(kissingToTrotting, standing);
|
||||||
|
|
||||||
transition(kissingSitting, sittingToTrotting, { exitAfter: 0, onlyDirectTo: trotting });
|
transition(kissingSitting, sittingToTrotting, { exitAfter: 0, onlyDirectTo: trotting });
|
||||||
transition(sittingToTrotting, standing);
|
transition(kissingSitting, sitting);
|
||||||
|
transition(sitting, kissingSitting, { exitAfter: 0, onlyDirectTo: kissingSitting });
|
||||||
|
|
||||||
transition(kissingLying, lyingToTrotting, { exitAfter: 0, onlyDirectTo: trotting });
|
transition(kissingLying, lyingToTrotting, { exitAfter: 0, onlyDirectTo: trotting });
|
||||||
transition(lyingToTrotting, standing);
|
transition(kissingLying, lying);
|
||||||
|
transition(lying, kissingLying, { exitAfter: 0, onlyDirectTo: kissingLying });
|
||||||
|
|
||||||
transition(anyState, trotting, { exitAfter: 0, keepTime: true });
|
transition(anyState, trotting, { exitAfter: 0, keepTime: true });
|
||||||
transition(anyState, flying, { exitAfter: 0, keepTime: true });
|
transition(anyState, flying, { exitAfter: 0, keepTime: true });
|
||||||
|
|||||||
@@ -132,6 +132,8 @@ export interface InternalApi extends InternalCommonApi {
|
|||||||
cancelUpdate(): Promise<void>;
|
cancelUpdate(): Promise<void>;
|
||||||
shutdownServer(value: boolean): Promise<void>;
|
shutdownServer(value: boolean): Promise<void>;
|
||||||
getTimings(): Promise<any[]>;
|
getTimings(): Promise<any[]>;
|
||||||
|
setTimingEnabled(isEnabled: boolean): Promise<void>;
|
||||||
|
getWorldPerfStats(): Promise<any>;
|
||||||
teleportTo(adminAccountId: string, targetAccountId: string): Promise<void>;
|
teleportTo(adminAccountId: string, targetAccountId: string): Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -822,6 +824,35 @@ export interface TimingEntry {
|
|||||||
name?: string;
|
name?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface WorldPerfStats {
|
||||||
|
lastUpdateTime: number;
|
||||||
|
maxUpdateTime: number;
|
||||||
|
minUpdateTime: number;
|
||||||
|
avgUpdateTime: number;
|
||||||
|
isSamplingEnabled: boolean;
|
||||||
|
clients: number;
|
||||||
|
movingEntities: number;
|
||||||
|
regionsCount: number;
|
||||||
|
mapsCount: number;
|
||||||
|
clientQueue: number;
|
||||||
|
controllersCount: number;
|
||||||
|
clientsWithAdds: number;
|
||||||
|
clientsWithUpdates: number;
|
||||||
|
clientsWithSays: number;
|
||||||
|
totalSays: number;
|
||||||
|
sent: number;
|
||||||
|
received: number;
|
||||||
|
sentPackets: number;
|
||||||
|
receivedPackets: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function defaultWorldPerfStats() {
|
||||||
|
return { lastUpdateTime: 0, maxUpdateTime: 0, minUpdateTime: 0, avgUpdateTime: 0, isSamplingEnabled: false,
|
||||||
|
clients: 0, movingEntities: 0, regionsCount: 0, mapsCount: 0, clientQueue: 0, controllersCount: 0,
|
||||||
|
clientsWithAdds: 0, clientsWithUpdates: 0, clientsWithSays: 0, totalSays: 0, sent: 0, received: 0,
|
||||||
|
sentPackets: 0, receivedPackets: 0 };
|
||||||
|
}
|
||||||
|
|
||||||
export type ModelTypes =
|
export type ModelTypes =
|
||||||
'accounts' | 'auths' | 'origins' | 'ponies' | 'accountAuths' | 'accountOrigins' | 'accountPonies';
|
'accounts' | 'auths' | 'origins' | 'ponies' | 'accountAuths' | 'accountOrigins' | 'accountPonies';
|
||||||
|
|
||||||
@@ -917,5 +948,7 @@ export interface IAdminServerActions {
|
|||||||
clearSessions(accountId: string): Promise<void>;
|
clearSessions(accountId: string): Promise<void>;
|
||||||
// other
|
// other
|
||||||
getTimings(serverId: string): Promise<any[]>;
|
getTimings(serverId: string): Promise<any[]>;
|
||||||
|
setTimingEnabled(serverId: string, isEnabled: boolean): Promise<void>;
|
||||||
|
getWorldPerfStats(serverId: string): Promise<any>;
|
||||||
teleportTo(accountId: string): Promise<void>;
|
teleportTo(accountId: string): Promise<void>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,16 +5,6 @@ import { getRegionGlobal, isInWaterAt } from './worldMap';
|
|||||||
import { tileWidth, tileHeight, PONY_TYPE, REGION_SIZE, REGION_WIDTH, REGION_HEIGHT } from './constants';
|
import { tileWidth, tileHeight, PONY_TYPE, REGION_SIZE, REGION_WIDTH, REGION_HEIGHT } from './constants';
|
||||||
import { isInTheAir, isFlying } from './entityUtils';
|
import { isInTheAir, isFlying } from './entityUtils';
|
||||||
|
|
||||||
let isCollidingCount = 0;
|
|
||||||
let isCollidingObjectCount = 0;
|
|
||||||
|
|
||||||
export function getCollisionStats() {
|
|
||||||
const stats = { isCollidingCount, isCollidingObjectCount };
|
|
||||||
isCollidingCount = 0;
|
|
||||||
isCollidingObjectCount = 0;
|
|
||||||
return stats;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isOutsideMap<T>(x: number, y: number, map: IMap<T>): boolean {
|
export function isOutsideMap<T>(x: number, y: number, map: IMap<T>): boolean {
|
||||||
return x < 0 || y < 0 || x >= map.width || y >= map.height;
|
return x < 0 || y < 0 || x >= map.width || y >= map.height;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1380,6 +1380,7 @@ export interface BodyAnimation {
|
|||||||
fps: number;
|
fps: number;
|
||||||
frames: BodyAnimationFrame[];
|
frames: BodyAnimationFrame[];
|
||||||
shadow?: BodyShadow[];
|
shadow?: BodyShadow[];
|
||||||
|
disableHeadTurnFrames: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface HeadAnimationFrame {
|
export interface HeadAnimationFrame {
|
||||||
@@ -1855,3 +1856,17 @@ export const enum UpdateFlags {
|
|||||||
SwitchRegion = 2048,
|
SwitchRegion = 2048,
|
||||||
// max 32768
|
// max 32768
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export let counterNow: () => number;
|
||||||
|
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
counterNow = performance.now;
|
||||||
|
} else {
|
||||||
|
const hrtime = process.hrtime;
|
||||||
|
const getNanoSeconds = () => {
|
||||||
|
const hr = hrtime();
|
||||||
|
return hr[0] * 1e9 + hr[1];
|
||||||
|
};
|
||||||
|
const nodeLoadTime = getNanoSeconds() - process.uptime() * 1e9;
|
||||||
|
counterNow = () => (getNanoSeconds() - nodeLoadTime) / 1e6;
|
||||||
|
}
|
||||||
|
|||||||
@@ -839,6 +839,7 @@ function state(
|
|||||||
name: '',
|
name: '',
|
||||||
loop: false,
|
loop: false,
|
||||||
fps: 24,
|
fps: 24,
|
||||||
|
disableHeadTurnFrames: 0,
|
||||||
frames: times(frames, i => ({
|
frames: times(frames, i => ({
|
||||||
...createBodyFrame([]),
|
...createBodyFrame([]),
|
||||||
head: (head || ones)[i],
|
head: (head || ones)[i],
|
||||||
|
|||||||
@@ -12,9 +12,9 @@ ng-template(#duplicatesPopover)
|
|||||||
button.btn.btn-sm.btn-default.ml-1((click)="cleanupDeleted()" title="Cleanup deleted events")
|
button.btn.btn-sm.btn-default.ml-1((click)="cleanupDeleted()" title="Cleanup deleted events")
|
||||||
fa-icon.mr-1([icon]="syncIcon" size="lg")
|
fa-icon.mr-1([icon]="syncIcon" size="lg")
|
||||||
| clear
|
| clear
|
||||||
button.btn.btn-sm.btn-default.ml-1((click)="removeEvents(1000 * 60 * 10)" title="Delete all older than 10 minutes")
|
button.btn.btn-sm.btn-default.ml-1((click)="removeEvents(1000 * 60 * 60 * 24)" title="Delete all older than 1 day")
|
||||||
fa-icon.mr-1([icon]="clockIcon" size="lg")
|
fa-icon.mr-1([icon]="clockIcon" size="lg")
|
||||||
| 10min
|
| 1day
|
||||||
button.btn.btn-sm.btn-default.ml-1((click)="removeEvents(1000 * 3)" title="Delete all events")
|
button.btn.btn-sm.btn-default.ml-1((click)="removeEvents(1000 * 3)" title="Delete all events")
|
||||||
fa-icon.mr-1([icon]="trashIcon" size="lg")
|
fa-icon.mr-1([icon]="trashIcon" size="lg")
|
||||||
| all
|
| all
|
||||||
|
|||||||
@@ -1,18 +1,22 @@
|
|||||||
div(style="position: relative;")
|
div(style="position: relative;")
|
||||||
div(style="position: absolute; top: -50px; right: 0;")
|
div(style="position: absolute; top: -50px; right: 0;")
|
||||||
b.p-2 {{server}}
|
b.p-2 {{server}}
|
||||||
.btn-group(*ngIf="server")
|
.btn-group(*ngIf="loaded")
|
||||||
button.btn.btn-default((click)="resetZoom()")
|
button.btn.btn-default((click)="resetZoom()")
|
||||||
| Reset zoom
|
| Reset zoom
|
||||||
button.btn.btn-default((click)="fitZoom()")
|
button.btn.btn-default((click)="fitZoom()")
|
||||||
| Fit zoom
|
| Fit zoom
|
||||||
button.btn.btn-default((click)="fullFrameZoom()")
|
button.btn.btn-default((click)="fullFrameZoom()")
|
||||||
| Full frame
|
| Full frame
|
||||||
button.btn.btn-default.ml-1(*ngIf="server" (click)="load(server)")
|
button.btn.btn-default.ml-1(*ngIf="loaded" (click)="load(server)")
|
||||||
| Refresh
|
| Refresh
|
||||||
|
button.btn.btn-default.ml-1(*ngIf="loaded" (click)="disable()")
|
||||||
|
| Disable
|
||||||
|
button.btn.btn-default.ml-1(*ngIf="!loaded && server" (click)="enable()")
|
||||||
|
| Enable
|
||||||
.btn-group.dropdown.ml-1(dropdown)
|
.btn-group.dropdown.ml-1(dropdown)
|
||||||
button.btn.btn-default.dropdown-toggle(dropdownToggle)
|
button.btn.btn-default.dropdown-toggle(dropdownToggle)
|
||||||
| Load
|
| Select
|
||||||
.dropdown-menu.dropdown-menu-right(*dropdownMenu)
|
.dropdown-menu.dropdown-menu-right(*dropdownMenu)
|
||||||
button.dropdown-item(*ngFor="let s of servers" (click)="load(s)")
|
button.dropdown-item(*ngFor="let s of servers" (click)="load(s)")
|
||||||
| {{s}}
|
| {{s}}
|
||||||
@@ -21,10 +25,87 @@ div(style="position: relative;")
|
|||||||
canvas(#canvas (mousemove)="mouseMove($event)" (wheel)="wheel($event)" (agDrag)="drag($event)")
|
canvas(#canvas (mousemove)="mouseMove($event)" (wheel)="wheel($event)" (agDrag)="drag($event)")
|
||||||
div(#tooltip style="display: none; position: absolute; left: 0; top: 0; padding: 2px 6px; background: #111; border-radius: 2px; font-size: 12px; white-space: pre; pointer-events: none;")
|
div(#tooltip style="display: none; position: absolute; left: 0; top: 0; padding: 2px 6px; background: #111; border-radius: 2px; font-size: 12px; white-space: pre; pointer-events: none;")
|
||||||
|
|
||||||
|
p
|
||||||
.float-right.text-muted
|
.float-right.text-muted
|
||||||
div total samples: {{timings.length}}
|
div samples: {{timings.length}}
|
||||||
|
|
||||||
table.table.table-sm(style="font-size: small; width: 1000px;")
|
br
|
||||||
|
.float-right.text-muted
|
||||||
|
div sampling: {{worldPerfStats.isSamplingEnabled}}
|
||||||
|
|
||||||
|
br
|
||||||
|
.float-right.text-muted
|
||||||
|
div avg update: {{worldPerfStats.avgUpdateTime.toFixed(2)}}
|
||||||
|
|
||||||
|
br
|
||||||
|
.float-right.text-muted
|
||||||
|
div last update: {{worldPerfStats.lastUpdateTime.toFixed(2)}}
|
||||||
|
|
||||||
|
br
|
||||||
|
.float-right.text-muted
|
||||||
|
div max update: {{worldPerfStats.maxUpdateTime.toFixed(2)}}
|
||||||
|
|
||||||
|
br
|
||||||
|
.float-right.text-muted
|
||||||
|
div min update: {{worldPerfStats.minUpdateTime.toFixed(2)}}
|
||||||
|
|
||||||
|
br
|
||||||
|
.float-right.text-muted
|
||||||
|
div moving entities: {{worldPerfStats.movingEntities}}
|
||||||
|
|
||||||
|
br
|
||||||
|
.float-right.text-muted
|
||||||
|
div controllers: {{worldPerfStats.controllersCount}}
|
||||||
|
|
||||||
|
br
|
||||||
|
.float-right.text-muted
|
||||||
|
div regions: {{worldPerfStats.regionsCount}}
|
||||||
|
|
||||||
|
br
|
||||||
|
.float-right.text-muted
|
||||||
|
div maps: {{worldPerfStats.mapsCount}}
|
||||||
|
|
||||||
|
br
|
||||||
|
.float-right.text-muted
|
||||||
|
div clients: {{worldPerfStats.clients}}
|
||||||
|
|
||||||
|
br
|
||||||
|
.float-right.text-muted
|
||||||
|
div clients with adds: {{worldPerfStats.clientsWithAdds}}
|
||||||
|
|
||||||
|
br
|
||||||
|
.float-right.text-muted
|
||||||
|
div clients with updates: {{worldPerfStats.clientsWithUpdates}}
|
||||||
|
|
||||||
|
br
|
||||||
|
.float-right.text-muted
|
||||||
|
div clients with says: {{worldPerfStats.clientsWithSays}}
|
||||||
|
|
||||||
|
br
|
||||||
|
.float-right.text-muted
|
||||||
|
div total says: {{worldPerfStats.totalSays}}
|
||||||
|
|
||||||
|
br
|
||||||
|
.float-right.text-muted
|
||||||
|
div client queue: {{worldPerfStats.clientQueue}}
|
||||||
|
|
||||||
|
br
|
||||||
|
.float-right.text-muted
|
||||||
|
div client sent: {{(worldPerfStats.sent / 1024).toFixed(2) + 'kb'}}
|
||||||
|
|
||||||
|
br
|
||||||
|
.float-right.text-muted
|
||||||
|
div client received: {{(worldPerfStats.received / 1024).toFixed(2) + 'kb'}}
|
||||||
|
|
||||||
|
br
|
||||||
|
.float-right.text-muted
|
||||||
|
div client sent packets: {{worldPerfStats.sentPackets}}
|
||||||
|
|
||||||
|
br
|
||||||
|
.float-right.text-muted
|
||||||
|
div client received packets: {{worldPerfStats.receivedPackets}}
|
||||||
|
|
||||||
|
table.table.table-sm(style="position: relative; bottom : 410px; font-size: small; width: 1000px;")
|
||||||
thead
|
thead
|
||||||
tr
|
tr
|
||||||
th.text-right Self Time
|
th.text-right Self Time
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Component, ViewChild, ElementRef } from '@angular/core';
|
import { Component, ViewChild, ElementRef } from '@angular/core';
|
||||||
import { AdminModel } from '../../../services/adminModel';
|
import { AdminModel } from '../../../services/adminModel';
|
||||||
import { TimingEntry, TimingEntryType } from '../../../../common/adminInterfaces';
|
import { TimingEntry, TimingEntryType, WorldPerfStats, defaultWorldPerfStats } from '../../../../common/adminInterfaces';
|
||||||
import { findById, pointInRect, clamp } from '../../../../common/utils';
|
import { findById, pointInRect, clamp } from '../../../../common/utils';
|
||||||
import { SERVER_FPS } from '../../../../common/constants';
|
import { SERVER_FPS } from '../../../../common/constants';
|
||||||
import { AgDragEvent } from '../../../shared/directives/agDrag';
|
import { AgDragEvent } from '../../../shared/directives/agDrag';
|
||||||
@@ -39,6 +39,7 @@ export class AdminReportsPerf {
|
|||||||
endTime = 0;
|
endTime = 0;
|
||||||
listing: ListingEntry[] = [];
|
listing: ListingEntry[] = [];
|
||||||
timings: TimingEntry[] = [];
|
timings: TimingEntry[] = [];
|
||||||
|
worldPerfStats = defaultWorldPerfStats();
|
||||||
private tooltips: Tooltip[] = [];
|
private tooltips: Tooltip[] = [];
|
||||||
private startTimeFrom = 0;
|
private startTimeFrom = 0;
|
||||||
private endTimeFrom = 0;
|
private endTimeFrom = 0;
|
||||||
@@ -53,10 +54,23 @@ export class AdminReportsPerf {
|
|||||||
}
|
}
|
||||||
}, 100);
|
}, 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setInterval(this.update, frameTime, this);
|
||||||
}
|
}
|
||||||
get servers() {
|
get servers() {
|
||||||
return this.model.state.gameServers.map(s => s.id);
|
return this.model.state.gameServers.map(s => s.id);
|
||||||
}
|
}
|
||||||
|
disable() {
|
||||||
|
this.loaded = false;
|
||||||
|
this.timings = [];
|
||||||
|
this.listing = [];
|
||||||
|
this.model.setTimingEnabled(this.server, false);
|
||||||
|
this.redraw();
|
||||||
|
}
|
||||||
|
async enable() {
|
||||||
|
this.loaded = true;
|
||||||
|
await this.load(this.server);
|
||||||
|
}
|
||||||
async load(server: string) {
|
async load(server: string) {
|
||||||
this.server = server;
|
this.server = server;
|
||||||
this.timings = [];
|
this.timings = [];
|
||||||
@@ -72,6 +86,17 @@ export class AdminReportsPerf {
|
|||||||
|
|
||||||
this.redraw();
|
this.redraw();
|
||||||
}
|
}
|
||||||
|
async update(self: AdminReportsPerf) {
|
||||||
|
if (self.server) {
|
||||||
|
const result = await self.model.getWorldPerfStats(self.server);
|
||||||
|
if (result) {
|
||||||
|
self.worldPerfStats = result as WorldPerfStats;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
self.worldPerfStats = defaultWorldPerfStats();
|
||||||
|
}
|
||||||
|
}
|
||||||
mouseMove(e: MouseEvent) {
|
mouseMove(e: MouseEvent) {
|
||||||
const rect = this.container.nativeElement.getBoundingClientRect() as DOMRect;
|
const rect = this.container.nativeElement.getBoundingClientRect() as DOMRect;
|
||||||
const x = e.pageX - rect.left;
|
const x = e.pageX - rect.left;
|
||||||
@@ -117,6 +142,10 @@ export class AdminReportsPerf {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
resetZoom() {
|
resetZoom() {
|
||||||
|
if (!this.timings.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const firstTime = this.timings[0].time;
|
const firstTime = this.timings[0].time;
|
||||||
const lastTime = this.timings[this.timings.length - 1].time;
|
const lastTime = this.timings[this.timings.length - 1].time;
|
||||||
this.startTime = firstTime - timePadding;
|
this.startTime = firstTime - timePadding;
|
||||||
@@ -125,6 +154,10 @@ export class AdminReportsPerf {
|
|||||||
this.lastZoom = 0;
|
this.lastZoom = 0;
|
||||||
}
|
}
|
||||||
fitZoom() {
|
fitZoom() {
|
||||||
|
if (!this.timings.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const firstTime = this.timings[0].time;
|
const firstTime = this.timings[0].time;
|
||||||
const lastTime = this.timings[this.timings.length - 1].time;
|
const lastTime = this.timings[this.timings.length - 1].time;
|
||||||
const totalTime = lastTime - firstTime;
|
const totalTime = lastTime - firstTime;
|
||||||
@@ -134,6 +167,10 @@ export class AdminReportsPerf {
|
|||||||
this.lastZoom = 1;
|
this.lastZoom = 1;
|
||||||
}
|
}
|
||||||
fullFrameZoom() {
|
fullFrameZoom() {
|
||||||
|
if (!this.timings.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const firstTime = this.timings[0].time;
|
const firstTime = this.timings[0].time;
|
||||||
const lastTime = firstTime + frameTime;
|
const lastTime = firstTime + frameTime;
|
||||||
this.startTime = firstTime - 2;
|
this.startTime = firstTime - 2;
|
||||||
@@ -245,6 +282,10 @@ export class AdminReportsPerf {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
recalcListing() {
|
recalcListing() {
|
||||||
|
if (!this.timings.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
interface Entry extends TimingEntry {
|
interface Entry extends TimingEntry {
|
||||||
excludedTime: number;
|
excludedTime: number;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -507,6 +507,14 @@ export class AdminModel {
|
|||||||
return this.server.getTimings(server)
|
return this.server.getTimings(server)
|
||||||
.catch(this.handleError);
|
.catch(this.handleError);
|
||||||
}
|
}
|
||||||
|
setTimingEnabled(server: string, isEnabled: boolean) {
|
||||||
|
return this.server.setTimingEnabled(server, isEnabled)
|
||||||
|
.catch(this.handleError);
|
||||||
|
}
|
||||||
|
getWorldPerfStats(server: string) {
|
||||||
|
return this.server.getWorldPerfStats(server)
|
||||||
|
.catch(this.handleError);
|
||||||
|
}
|
||||||
teleportTo(accountId: string) {
|
teleportTo(accountId: string) {
|
||||||
return this.server.teleportTo(accountId)
|
return this.server.teleportTo(accountId)
|
||||||
.catch(this.handleError);
|
.catch(this.handleError);
|
||||||
|
|||||||
@@ -685,6 +685,7 @@ function toBodyAnimation({ name, loop, fps, frames }: BodyAnimation, full: boole
|
|||||||
loop,
|
loop,
|
||||||
fps,
|
fps,
|
||||||
shadow,
|
shadow,
|
||||||
|
disableHeadTurnFrames: 0,
|
||||||
frames: flatMap(frames, f => repeat(full ? f.duration : 1, {
|
frames: flatMap(frames, f => repeat(full ? f.duration : 1, {
|
||||||
body: f.body,
|
body: f.body,
|
||||||
head: f.head,
|
head: f.head,
|
||||||
@@ -706,7 +707,7 @@ function toBodyAnimation({ name, loop, fps, frames }: BodyAnimation, full: boole
|
|||||||
backLegY: switchFarClose ? f.backFarLegY : f.backLegY,
|
backLegY: switchFarClose ? f.backFarLegY : f.backLegY,
|
||||||
backFarLegX: switchFarClose ? f.backLegX : f.backFarLegX,
|
backFarLegX: switchFarClose ? f.backLegX : f.backFarLegX,
|
||||||
backFarLegY: switchFarClose ? f.backLegY : f.backFarLegY,
|
backFarLegY: switchFarClose ? f.backLegY : f.backFarLegY,
|
||||||
})),
|
}))
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -547,6 +547,16 @@ export class AdminServerActions implements IAdminServerActions, SocketServer {
|
|||||||
return server.api.getTimings();
|
return server.api.getTimings();
|
||||||
}
|
}
|
||||||
@Method({ promise: true })
|
@Method({ promise: true })
|
||||||
|
async setTimingEnabled(serverId: string, isEnabled: boolean) {
|
||||||
|
const server = getServer(serverId);
|
||||||
|
server.api.setTimingEnabled(isEnabled);
|
||||||
|
}
|
||||||
|
@Method({ promise: true })
|
||||||
|
async getWorldPerfStats(serverId: string) {
|
||||||
|
const server = getServer(serverId);
|
||||||
|
return server.api.getWorldPerfStats();
|
||||||
|
}
|
||||||
|
@Method({ promise: true })
|
||||||
async teleportTo(accountId: string) {
|
async teleportTo(accountId: string) {
|
||||||
const adminAccountId = this.account._id.toString();
|
const adminAccountId = this.account._id.toString();
|
||||||
await forAllGameServers(server => server.api.teleportTo(adminAccountId, accountId));
|
await forAllGameServers(server => server.api.teleportTo(adminAccountId, accountId));
|
||||||
|
|||||||
@@ -15,10 +15,11 @@ import { createReloadSettings } from './internal-common';
|
|||||||
import { UserError } from '../userError';
|
import { UserError } from '../userError';
|
||||||
import { liveSettings } from '../liveSettings';
|
import { liveSettings } from '../liveSettings';
|
||||||
import { formatDuration, invalidEnum } from '../../common/utils';
|
import { formatDuration, invalidEnum } from '../../common/utils';
|
||||||
import { timingEntries } from '../timing';
|
import { timingEntries, setTimingEnabled } from '../timing';
|
||||||
import { toPairs, groupBy } from 'lodash';
|
import { toPairs, groupBy } from 'lodash';
|
||||||
import { getSizeOfMap } from '../serverMap';
|
import { getSizeOfMap } from '../serverMap';
|
||||||
import { teleportTo } from '../playerUtils';
|
import { teleportTo } from '../playerUtils';
|
||||||
|
import { getWorldPerfStats } from '../worldPerfStats';
|
||||||
|
|
||||||
export const createAccountChanged =
|
export const createAccountChanged =
|
||||||
(world: World, tokens: TokenService, findAccount: FindAccountSafe) =>
|
(world: World, tokens: TokenService, findAccount: FindAccountSafe) =>
|
||||||
@@ -269,6 +270,8 @@ export function createInternalApi(
|
|||||||
shutdownServer: createShutdownServer(world, live),
|
shutdownServer: createShutdownServer(world, live),
|
||||||
accountHidden: createHiddenStats(hiding),
|
accountHidden: createHiddenStats(hiding),
|
||||||
getTimings: async () => timingEntries(),
|
getTimings: async () => timingEntries(),
|
||||||
|
setTimingEnabled: async (isEnabled: boolean) => setTimingEnabled(isEnabled),
|
||||||
|
getWorldPerfStats: async () => getWorldPerfStats(),
|
||||||
teleportTo: createTeleportTo(world),
|
teleportTo: createTeleportTo(world),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,7 +31,8 @@ import {
|
|||||||
import { replaceEmojis } from '../client/emoji';
|
import { replaceEmojis } from '../client/emoji';
|
||||||
import { expression, parseExpression } from '../common/expressionUtils';
|
import { expression, parseExpression } from '../common/expressionUtils';
|
||||||
import {
|
import {
|
||||||
canBoopOrKiss, isPonySitting, isPonyStanding, getBoopRect, canStand, isPonyFlying, setPonyState, canSit, canLie, getkissRect, getSneezeRect
|
canBoopOrKiss, isPonySitting, isPonyStanding, getBoopRect, canStand, isPonyFlying, setPonyState, canSit,
|
||||||
|
canLie, getkissRect, getSneezeRect
|
||||||
} from '../common/entityUtils';
|
} from '../common/entityUtils';
|
||||||
import { withBorder } from '../common/rect';
|
import { withBorder } from '../common/rect';
|
||||||
import { isOnlineFriend } from './services/friends';
|
import { isOnlineFriend } from './services/friends';
|
||||||
@@ -407,8 +408,10 @@ export function useHeldItem(client: IClient) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function canPerformAction(client: IClient) {
|
export function canPerformAction(client: IClient, now?: number) {
|
||||||
const now = Date.now();
|
if (!now) {
|
||||||
|
now = Date.now();
|
||||||
|
}
|
||||||
return client.lastExpressionAction < now && client.lastBoopOrKissAction < now;
|
return client.lastExpressionAction < now && client.lastBoopOrKissAction < now;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -420,9 +423,7 @@ export function updateEntityPlayerState(client: IClient, entity: ServerEntity) {
|
|||||||
// actions
|
// actions
|
||||||
|
|
||||||
export function turnHead(client: IClient) {
|
export function turnHead(client: IClient) {
|
||||||
if (canPerformAction(client)) {
|
|
||||||
updateEntityState(client.pony, client.pony.state ^ EntityState.HeadTurned);
|
updateEntityState(client.pony, client.pony.state ^ EntityState.HeadTurned);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const purpleGrapeTypes = entities.grapesPurple.map(x => x.type);
|
const purpleGrapeTypes = entities.grapesPurple.map(x => x.type);
|
||||||
@@ -471,7 +472,7 @@ function boopEntity(client: IClient, rect: Rect, isOnlyBooping: boolean) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function boop(client: IClient, now: number) {
|
export function boop(client: IClient, now: number) {
|
||||||
if (canPerformAction(client) && canBoopOrKiss(client.pony)) {
|
if (canPerformAction(client, now) && canBoopOrKiss(client.pony)) {
|
||||||
cancelEntityExpression(client.pony);
|
cancelEntityExpression(client.pony);
|
||||||
sendAction(client.pony, Action.Boop);
|
sendAction(client.pony, Action.Boop);
|
||||||
boopEntity(client, getBoopRect(client.pony), false);
|
boopEntity(client, getBoopRect(client.pony), false);
|
||||||
@@ -480,20 +481,21 @@ export function boop(client: IClient, now: number) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function kiss(client: IClient, now: number) {
|
export function kiss(client: IClient, now: number) {
|
||||||
if (canPerformAction(client) && canBoopOrKiss(client.pony)) {
|
if (canPerformAction(client, now) && canBoopOrKiss(client.pony)) {
|
||||||
cancelEntityExpression(client.pony);
|
cancelEntityExpression(client.pony);
|
||||||
sendAction(client.pony, Action.Kiss);
|
sendAction(client.pony, Action.Kiss);
|
||||||
boopEntity(client, getkissRect(client.pony), false);
|
boopEntity(client, getkissRect(client.pony), false);
|
||||||
client.lastBoopOrKissAction = now + 3350;
|
client.lastBoopOrKissAction = now + 3400;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function sneeze(client: IClient) {
|
export function sneeze(client: IClient) {
|
||||||
if (canPerformAction(client)) {
|
const now = Date.now();
|
||||||
|
if (canPerformAction(client, now)) {
|
||||||
cancelEntityExpression(client.pony);
|
cancelEntityExpression(client.pony);
|
||||||
sendAction(client.pony, Action.Sneeze);
|
sendAction(client.pony, Action.Sneeze);
|
||||||
boopEntity(client, getSneezeRect(client.pony), true);
|
boopEntity(client, getSneezeRect(client.pony), true);
|
||||||
client.lastExpressionAction = Date.now() + 750;
|
client.lastExpressionAction = now + 750;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -557,10 +559,11 @@ export function fly(client: IClient) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function expressionAction(client: IClient, action: Action) {
|
export function expressionAction(client: IClient, action: Action) {
|
||||||
if (canPerformAction(client) && isExpressionAction(action)) {
|
const now = Date.now();
|
||||||
|
if (canPerformAction(client, now) && isExpressionAction(action)) {
|
||||||
cancelEntityExpression(client.pony);
|
cancelEntityExpression(client.pony);
|
||||||
sendAction(client.pony, action);
|
sendAction(client.pony, action);
|
||||||
client.lastExpressionAction = Date.now() + 750;
|
client.lastExpressionAction = now + 750;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -836,7 +839,6 @@ export function switchTool(client: IClient, reverse: boolean) {
|
|||||||
const newIndex = reverse ? (index === -1 ? tools.length - 1 : index - 1) : ((index + 1) % tools.length);
|
const newIndex = reverse ? (index === -1 ? tools.length - 1 : index - 1) : ((index + 1) % tools.length);
|
||||||
const tool = tools[newIndex];
|
const tool = tools[newIndex];
|
||||||
holdItem(client.pony, tool.type);
|
holdItem(client.pony, tool.type);
|
||||||
// console.log('isMobile ' + client.isMobile);
|
|
||||||
const text = (client.isMobile && tool.textMobile) ? tool.textMobile : tool.text;
|
const text = (client.isMobile && tool.textMobile) ? tool.textMobile : tool.text;
|
||||||
saySystem(client, text);
|
saySystem(client, text);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -320,6 +320,7 @@ export class ServerActions implements IServerActions, SocketServer {
|
|||||||
|
|
||||||
let totalEditableEntities = 0;
|
let totalEditableEntities = 0;
|
||||||
|
|
||||||
|
// TODO: optimize
|
||||||
for (const region of this.map.regions) {
|
for (const region of this.map.regions) {
|
||||||
for (const entity of region.entities) {
|
for (const entity of region.entities) {
|
||||||
if (hasFlag(entity.state, EntityState.Editable)) {
|
if (hasFlag(entity.state, EntityState.Editable)) {
|
||||||
|
|||||||
+37
-22
@@ -1,37 +1,41 @@
|
|||||||
import { TimingEntry, TimingEntryType } from '../common/adminInterfaces';
|
import { TimingEntry, TimingEntryType } from '../common/adminInterfaces';
|
||||||
|
import { MINUTE } from '../common/constants';
|
||||||
|
import { counterNow } from '../common/interfaces';
|
||||||
|
|
||||||
const ENABLED = true;
|
const ENTRIES_LIMIT = 20000;
|
||||||
const ENTRIES_LIMIT = 50000;
|
|
||||||
|
|
||||||
const entries: TimingEntry[] = [];
|
let entries: TimingEntry[] = [];
|
||||||
let entriesCount = 0;
|
let entriesCount = 0;
|
||||||
|
let isProfilingEnabled = false;
|
||||||
|
let lastFetchTime = Date.now();
|
||||||
|
|
||||||
let now: () => number;
|
export function getTimingEnabled() {
|
||||||
|
return isProfilingEnabled;
|
||||||
if (typeof window !== 'undefined') {
|
|
||||||
now = performance.now;
|
|
||||||
} else {
|
|
||||||
const hrtime = process.hrtime;
|
|
||||||
const getNanoSeconds = () => {
|
|
||||||
const hr = hrtime();
|
|
||||||
return hr[0] * 1e9 + hr[1];
|
|
||||||
};
|
|
||||||
const nodeLoadTime = getNanoSeconds() - process.uptime() * 1e9;
|
|
||||||
now = () => (getNanoSeconds() - nodeLoadTime) / 1e6;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ENABLED) {
|
export function setTimingEnabled(isEnabled: boolean) {
|
||||||
|
if (isProfilingEnabled === isEnabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
isProfilingEnabled = isEnabled;
|
||||||
|
|
||||||
|
if (isEnabled) {
|
||||||
for (let i = 0; i < ENTRIES_LIMIT; i++) {
|
for (let i = 0; i < ENTRIES_LIMIT; i++) {
|
||||||
entries.push({ type: 0, time: 0, name: undefined });
|
entries.push({ type: 0, time: 0, name: undefined });
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
entries = [];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function timingStart(name: string) {
|
export function timingStart(name: string) {
|
||||||
if (ENABLED) {
|
if (isProfilingEnabled) {
|
||||||
if (entriesCount < ENTRIES_LIMIT) {
|
if (entriesCount < ENTRIES_LIMIT) {
|
||||||
const entry = entries[entriesCount];
|
const entry = entries[entriesCount];
|
||||||
entry.type = TimingEntryType.Start;
|
entry.type = TimingEntryType.Start;
|
||||||
entry.time = now();
|
entry.time = counterNow();
|
||||||
entry.name = name;
|
entry.name = name;
|
||||||
entriesCount++;
|
entriesCount++;
|
||||||
} else {
|
} else {
|
||||||
@@ -41,11 +45,11 @@ export function timingStart(name: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function timingEnd() {
|
export function timingEnd() {
|
||||||
if (ENABLED) {
|
if (isProfilingEnabled) {
|
||||||
if (entriesCount < ENTRIES_LIMIT) {
|
if (entriesCount < ENTRIES_LIMIT) {
|
||||||
const entry = entries[entriesCount];
|
const entry = entries[entriesCount];
|
||||||
entry.type = TimingEntryType.End;
|
entry.type = TimingEntryType.End;
|
||||||
entry.time = now();
|
entry.time = counterNow();
|
||||||
entry.name = undefined;
|
entry.name = undefined;
|
||||||
entriesCount++;
|
entriesCount++;
|
||||||
} else {
|
} else {
|
||||||
@@ -55,11 +59,22 @@ export function timingEnd() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function timingReset() {
|
export function timingReset() {
|
||||||
if (ENABLED) {
|
|
||||||
entriesCount = 0;
|
entriesCount = 0;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function timingEntries() {
|
export function timingEntries() {
|
||||||
|
lastFetchTime = Date.now();
|
||||||
|
if (!isProfilingEnabled) {
|
||||||
|
setTimingEnabled(true);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
return entries.slice(0, entriesCount);
|
return entries.slice(0, entriesCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function timingUpdate() {
|
||||||
|
if (isProfilingEnabled) {
|
||||||
|
if (Date.now() - lastFetchTime > MINUTE) {
|
||||||
|
setTimingEnabled(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+23
-17
@@ -1,7 +1,7 @@
|
|||||||
import { getWriterBuffer } from 'ag-sockets';
|
import { getWriterBuffer } from 'ag-sockets';
|
||||||
import { remove, compact } from 'lodash';
|
import { remove, compact } from 'lodash';
|
||||||
import {
|
import {
|
||||||
TileType, WorldState, Season, Holiday, WorldStateFlags, LeaveReason, NotificationFlags, UpdateFlags, Action,
|
TileType, WorldState, Season, Holiday, WorldStateFlags, LeaveReason, NotificationFlags, UpdateFlags, Action, counterNow,
|
||||||
} from '../common/interfaces';
|
} from '../common/interfaces';
|
||||||
import { removeItem, distance, clamp, randomPoint, includes, fromNow } from '../common/utils';
|
import { removeItem, distance, clamp, randomPoint, includes, fromNow } from '../common/utils';
|
||||||
import { HOUR_LENGTH, DAY_LENGTH } from '../common/timeUtils';
|
import { HOUR_LENGTH, DAY_LENGTH } from '../common/timeUtils';
|
||||||
@@ -31,12 +31,12 @@ import {
|
|||||||
import { roundPosition, roundPositionXMidPixel, roundPositionYMidPixel } from '../common/positionUtils';
|
import { roundPosition, roundPositionXMidPixel, roundPositionYMidPixel } from '../common/positionUtils';
|
||||||
import { logger } from './logger';
|
import { logger } from './logger';
|
||||||
import { updateCamera, centerCameraOn } from '../common/camera';
|
import { updateCamera, centerCameraOn } from '../common/camera';
|
||||||
import { timingStart, timingEnd } from './timing';
|
import { timingStart, timingEnd, timingUpdate } from './timing';
|
||||||
import { getRegionGlobal, getTile } from '../common/worldMap';
|
import { getRegionGlobal, getTile } from '../common/worldMap';
|
||||||
import { getEntityTypeName } from '../common/entities';
|
import { getEntityTypeName } from '../common/entities';
|
||||||
import { toFriendOnline, toFriendOffline, FriendsService } from './services/friends';
|
import { toFriendOnline, toFriendOffline, FriendsService } from './services/friends';
|
||||||
// import { Pool, createPool } from './pool';
|
// import { Pool, createPool } from './pool';
|
||||||
import { isStaticCollision, getCollisionStats, fixCollision, updatePosition } from '../common/collision';
|
import { isStaticCollision, fixCollision, updatePosition } from '../common/collision';
|
||||||
import { HidingService } from './services/hiding';
|
import { HidingService } from './services/hiding';
|
||||||
import { generateRegionCollider } from '../common/region';
|
import { generateRegionCollider } from '../common/region';
|
||||||
import { updateTileIndices } from '../client/tileUtils';
|
import { updateTileIndices } from '../client/tileUtils';
|
||||||
@@ -44,6 +44,7 @@ import { removeEntityFromRegion } from './serverRegion';
|
|||||||
import { createIslandMap } from './maps/islandMap';
|
import { createIslandMap } from './maps/islandMap';
|
||||||
import { createHouseMap } from './maps/houseMap';
|
import { createHouseMap } from './maps/houseMap';
|
||||||
import { updateMainMapSeason } from './maps/mainMap';
|
import { updateMainMapSeason } from './maps/mainMap';
|
||||||
|
import { updateWorldPerfStats } from './worldPerfStats';
|
||||||
|
|
||||||
interface MapSwitch {
|
interface MapSwitch {
|
||||||
map: ServerMap;
|
map: ServerMap;
|
||||||
@@ -287,8 +288,16 @@ export class World {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
update(delta: number, now: number) {
|
update(delta: number, now: number) {
|
||||||
|
const statsStart = counterNow();
|
||||||
|
let movingEntities = 0;
|
||||||
|
let regionsCount = 0;
|
||||||
|
let mapsCount = 0;
|
||||||
|
let controllersCount = 0;
|
||||||
|
|
||||||
const started = Date.now();
|
const started = Date.now();
|
||||||
|
|
||||||
|
timingUpdate();
|
||||||
|
|
||||||
timingStart('world.update()');
|
timingStart('world.update()');
|
||||||
|
|
||||||
resetEncodeUpdate();
|
resetEncodeUpdate();
|
||||||
@@ -315,7 +324,10 @@ export class World {
|
|||||||
|
|
||||||
timingStart('update positions');
|
timingStart('update positions');
|
||||||
for (const map of this.maps) {
|
for (const map of this.maps) {
|
||||||
|
++mapsCount;
|
||||||
for (const region of map.regions) {
|
for (const region of map.regions) {
|
||||||
|
++regionsCount;
|
||||||
|
|
||||||
// TODO: update only moving entities, separate list of movingEntities
|
// TODO: update only moving entities, separate list of movingEntities
|
||||||
for (const entity of region.movables) {
|
for (const entity of region.movables) {
|
||||||
// TODO: make sure timestamp is initialized if entity is moving
|
// TODO: make sure timestamp is initialized if entity is moving
|
||||||
@@ -323,6 +335,7 @@ export class World {
|
|||||||
|
|
||||||
if (delta > 0) {
|
if (delta > 0) {
|
||||||
if (entity.vx !== 0 || entity.vy !== 0) {
|
if (entity.vx !== 0 || entity.vy !== 0) {
|
||||||
|
++movingEntities;
|
||||||
updatePosition(entity, delta, map);
|
updatePosition(entity, delta, map);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -349,6 +362,7 @@ export class World {
|
|||||||
|
|
||||||
for (const map of this.maps) {
|
for (const map of this.maps) {
|
||||||
for (const controller of map.controllers) {
|
for (const controller of map.controllers) {
|
||||||
|
++controllersCount;
|
||||||
controller.update(deltaSeconds, nowSeconds);
|
controller.update(deltaSeconds, nowSeconds);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -408,9 +422,7 @@ export class World {
|
|||||||
totalSays += saysQueue.length;
|
totalSays += saysQueue.length;
|
||||||
|
|
||||||
setupTiming(client);
|
setupTiming(client);
|
||||||
timingStart('client.update()');
|
|
||||||
client.update(unsubscribes, subscribes, updateBuffer, regionUpdates, saysQueue);
|
client.update(unsubscribes, subscribes, updateBuffer, regionUpdates, saysQueue);
|
||||||
timingEnd();
|
|
||||||
clearTiming(client);
|
clearTiming(client);
|
||||||
|
|
||||||
resetClientUpdates(client);
|
resetClientUpdates(client);
|
||||||
@@ -426,17 +438,16 @@ export class World {
|
|||||||
}
|
}
|
||||||
timingEnd();
|
timingEnd();
|
||||||
|
|
||||||
const { isCollidingCount, isCollidingObjectCount } = getCollisionStats();
|
timingStart(`cleanupOfflineClients`);
|
||||||
|
|
||||||
timingStart(`adds [${clientsWithAdds}]\n` +
|
|
||||||
`updates [${clientsWithUpdates}]\n` +
|
|
||||||
`says [${totalSays} / ${clientsWithSays}]\n` +
|
|
||||||
`sockets [${this.socketStatsText()}]\n` +
|
|
||||||
`collisions [${isCollidingObjectCount} / ${isCollidingCount}]`);
|
|
||||||
this.cleanupOfflineClients();
|
this.cleanupOfflineClients();
|
||||||
timingEnd();
|
timingEnd();
|
||||||
|
|
||||||
timingEnd();
|
timingEnd();
|
||||||
|
|
||||||
|
const { sent, received, sentPackets, receivedPackets } = this.socketStats.stats();
|
||||||
|
updateWorldPerfStats(statsStart, this.clients.length, movingEntities, regionsCount, mapsCount, this.joinQueue.length,
|
||||||
|
controllersCount, clientsWithAdds, clientsWithUpdates, clientsWithSays, totalSays,
|
||||||
|
sent, received, sentPackets, receivedPackets);
|
||||||
}
|
}
|
||||||
sparseUpdate(now: number) {
|
sparseUpdate(now: number) {
|
||||||
timingStart('world.sparseUpdate()');
|
timingStart('world.sparseUpdate()');
|
||||||
@@ -536,11 +547,6 @@ export class World {
|
|||||||
|
|
||||||
timingEnd();
|
timingEnd();
|
||||||
}
|
}
|
||||||
private socketStatsText() {
|
|
||||||
const { sent, received, sentPackets, receivedPackets } = this.socketStats.stats();
|
|
||||||
return `sent: ${(sent / 1024).toFixed(2)} kb (${sentPackets}), ` +
|
|
||||||
`recv: ${(received / 1024).toFixed(2)} kb (${receivedPackets})`;
|
|
||||||
}
|
|
||||||
updatesStats() {
|
updatesStats() {
|
||||||
timingStart('updatesStats()');
|
timingStart('updatesStats()');
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,66 @@
|
|||||||
|
import { defaultWorldPerfStats } from '../common/adminInterfaces';
|
||||||
|
import { counterNow } from '../common/interfaces';
|
||||||
|
import { getTimingEnabled } from './timing';
|
||||||
|
|
||||||
|
const FRAME_HISTORY_SIZE = 25;
|
||||||
|
|
||||||
|
let stats = defaultWorldPerfStats();
|
||||||
|
const frameHistory: number[] = [];
|
||||||
|
frameHistory.length = FRAME_HISTORY_SIZE;
|
||||||
|
frameHistory.fill(-1);
|
||||||
|
let frameHistoryPtr = 0;
|
||||||
|
|
||||||
|
function addFrameToHistory(time: number) {
|
||||||
|
frameHistory[frameHistoryPtr++] = time;
|
||||||
|
if (frameHistoryPtr === FRAME_HISTORY_SIZE) {
|
||||||
|
frameHistoryPtr = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function updateWorldPerfStats(
|
||||||
|
frameStart: number, clients: number, movingEntities: number, regionsCount: number, mapsCount: number, clientQueue: number,
|
||||||
|
controllersCount: number, clientsWithAdds: number, clientsWithUpdates: number, clientsWithSays: number, totalSays: number,
|
||||||
|
sent: number, received: number, sentPackets: number, receivedPackets: number) {
|
||||||
|
const time = counterNow() - frameStart;
|
||||||
|
stats.lastUpdateTime = time;
|
||||||
|
addFrameToHistory(time);
|
||||||
|
stats.isSamplingEnabled = getTimingEnabled();
|
||||||
|
stats.clients = clients;
|
||||||
|
stats.movingEntities = movingEntities;
|
||||||
|
stats.regionsCount = regionsCount;
|
||||||
|
stats.mapsCount = mapsCount;
|
||||||
|
stats.clientQueue = clientQueue;
|
||||||
|
stats.controllersCount = controllersCount;
|
||||||
|
stats.clientsWithAdds = clientsWithAdds;
|
||||||
|
stats.clientsWithUpdates = clientsWithUpdates;
|
||||||
|
stats.clientsWithSays = clientsWithSays;
|
||||||
|
stats.totalSays = totalSays;
|
||||||
|
stats.sent = sent;
|
||||||
|
stats.received = received;
|
||||||
|
stats.sentPackets = sentPackets;
|
||||||
|
stats.receivedPackets = receivedPackets;
|
||||||
|
|
||||||
|
let avg = 0, nonZero = 0;
|
||||||
|
stats.maxUpdateTime = Number.MIN_VALUE;
|
||||||
|
stats.minUpdateTime = Number.MAX_VALUE;
|
||||||
|
|
||||||
|
for (const f of frameHistory) {
|
||||||
|
if (f < 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (f < stats.minUpdateTime) {
|
||||||
|
stats.minUpdateTime = f;
|
||||||
|
}
|
||||||
|
if (f > stats.maxUpdateTime) {
|
||||||
|
stats.maxUpdateTime = f;
|
||||||
|
}
|
||||||
|
avg += f;
|
||||||
|
++nonZero;
|
||||||
|
}
|
||||||
|
|
||||||
|
stats.avgUpdateTime = avg / nonZero;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getWorldPerfStats() {
|
||||||
|
return stats;
|
||||||
|
}
|
||||||
@@ -20,7 +20,7 @@ const sets: [keyof typeof sprites, number, number, (number[] | null)[]][] = [
|
|||||||
['hornsBehind', -1, 1, [null, null, null, null, [3, 5], [3, 7], [3, 9, 9], [3, 9, 13], [3, 9, 13], [3, 7], [3, 11, 5, 5], [3, 9, 5, 5], [3, 9, 5], [3, 7], []]],
|
['hornsBehind', -1, 1, [null, null, null, null, [3, 5], [3, 7], [3, 9, 9], [3, 9, 13], [3, 9, 13], [3, 7], [3, 11, 5, 5], [3, 9, 5, 5], [3, 9, 5], [3, 7], []]],
|
||||||
['ears', -1, 1, [[3, 7], [3, 9], [3, 9], [3, 5], [3, 7, 7], [3, 7, 9]]],
|
['ears', -1, 1, [[3, 7], [3, 9], [3, 9], [3, 5], [3, 7, 7], [3, 7, 9]]],
|
||||||
['earsFar', -1, 1, [[3, 7], [3, 9], [3, 9], [3, 5], [3, 7, 7], [3, 7, 9]]],
|
['earsFar', -1, 1, [[3, 7], [3, 9], [3, 9], [3, 5], [3, 7, 7], [3, 7, 9]]],
|
||||||
['frontLegHooves', 1, 39, [null, [3], [3], [5], [5], [5], [3]]],
|
['frontLegHooves', 1, 40, [null, [3], [3], [5], [5], [5], [3]]],
|
||||||
['backLegHooves', 1, 27, [null, [3], [3], [5], [3]]],
|
['backLegHooves', 1, 27, [null, [3], [3], [5], [3]]],
|
||||||
['wings', 1, 13, [null, [3, 5, 9], [5], [3, 5, 9], [3]]],
|
['wings', 1, 13, [null, [3, 5, 9], [5], [3, 5, 9], [3]]],
|
||||||
['tails', 0, 3, [null, [3, 11, 13, 7, 5, 5], [3, 13, 9, 7, 5, 5], [3, 11, 9, 7, 5, 5], [3, 13, 11, 7, 5, 5], [3, 9, 13, 7, 5, 5], [3, 11, 13, 7, 5, 5, 5], [5, 13, 11, 7, 7], [3, 11, 13, 5, 7, 5], [3, 11, 11, 7, 5, 5, 11], [3, 11, 13, 5], [3, 11, 11, 7, 5, 5], [3, 9, 9, 7, 5], [3, 5, 9, 5], [5, 11, 9, 7, 7], [5, 7], [3, 5, 7], [5, 11, 9, 7, 7], [7, 13, 11, 9, 9], [5, 7], [3, 11, 13, 7, 5, 5], [3, 9, 11, 7, 5, 5], [3, 11, 13, 7, 5, 5], [3, 9, 11, 7, 5, 5], [3, 13, 7], [3, 5, 13, 13, 11, 11], [3, 9, 13, 13, 13, 11], [3, 7, 5, 13, 5], [5, 13, 9, 9, 7, 7], [5, 13, 9, 9, 7, 7], [3, 5, 7]]],
|
['tails', 0, 3, [null, [3, 11, 13, 7, 5, 5], [3, 13, 9, 7, 5, 5], [3, 11, 9, 7, 5, 5], [3, 13, 11, 7, 5, 5], [3, 9, 13, 7, 5, 5], [3, 11, 13, 7, 5, 5, 5], [5, 13, 11, 7, 7], [3, 11, 13, 5, 7, 5], [3, 11, 11, 7, 5, 5, 11], [3, 11, 13, 5], [3, 11, 11, 7, 5, 5], [3, 9, 9, 7, 5], [3, 5, 9, 5], [5, 11, 9, 7, 7], [5, 7], [3, 5, 7], [5, 11, 9, 7, 7], [7, 13, 11, 9, 9], [5, 7], [3, 11, 13, 7, 5, 5], [3, 9, 11, 7, 5, 5], [3, 11, 13, 7, 5, 5], [3, 9, 11, 7, 5, 5], [3, 13, 7], [3, 5, 13, 13, 11, 11], [3, 9, 13, 13, 13, 11], [3, 7, 5, 13, 5], [5, 13, 9, 9, 7, 7], [5, 13, 9, 9, 7, 7], [3, 5, 7]]],
|
||||||
@@ -29,13 +29,13 @@ const sets: [keyof typeof sprites, number, number, (number[] | null)[]][] = [
|
|||||||
['facialHairBehind', -1, 1, [null, [3], [3, 7], null, [3, 7, 9], [3, 5], [3, 7, 9], [3, 7, 17], [3, 17, 7], [3, 13, 7, 7], [3, 13, 7, 9], [3, 13, 7, 9], [3, 13, 7, 9], [5, 13, 9, 13], [5, 13, 9, 13], [3, 17, 7, 11]]],
|
['facialHairBehind', -1, 1, [null, [3], [3, 7], null, [3, 7, 9], [3, 5], [3, 7, 9], [3, 7, 17], [3, 17, 7], [3, 13, 7, 7], [3, 13, 7, 9], [3, 13, 7, 9], [3, 13, 7, 9], [5, 13, 9, 13], [5, 13, 9, 13], [3, 17, 7, 11]]],
|
||||||
['headAccessoriesBehind', -1, 1, [null, [3, 5], [3, 9, 9, 9], [3, 5, 7, 9, 7], [3, 5], [3, 7], [3, 7, 13], [3, 11, 11], [3, 11, 13], [3, 11, 7, 9, 9], [3, 9, 13], [5, 11, 13], [3, 7], [3, 9, 13], [3, 9], [3, 9], [3, 7], [3, 7], [3, 7, 5, 5], [3, 7, 11]]],
|
['headAccessoriesBehind', -1, 1, [null, [3, 5], [3, 9, 9, 9], [3, 5, 7, 9, 7], [3, 5], [3, 7], [3, 7, 13], [3, 11, 11], [3, 11, 13], [3, 11, 7, 9, 9], [3, 9, 13], [5, 11, 13], [3, 7], [3, 9, 13], [3, 9], [3, 9], [3, 7], [3, 7], [3, 7, 5, 5], [3, 7, 11]]],
|
||||||
['neckAccessories', 1, 16, [null, [3, 5], [3, 5, 9, 5], [3, 5, 7, 13, 7], [3, 5], [3, 7, 7, 5, 5], [3, 5, 7], [3, 5], [3], [3], [3, 5, 9, 5], [3, 5, 9], [3, 9], [3], [3]]],
|
['neckAccessories', 1, 16, [null, [3, 5], [3, 5, 9, 5], [3, 5, 7, 13, 7], [3, 5], [3, 7, 7, 5, 5], [3, 5, 7], [3, 5], [3], [3], [3, 5, 9, 5], [3, 5, 9], [3, 9], [3], [3]]],
|
||||||
['frontLegAccessories', 1, 39, [null, [3, 13, 13, 13, 13]]],
|
['frontLegAccessories', 1, 40, [null, [3, 13, 13, 13, 13]]],
|
||||||
['backLegAccessories', 1, 27, [null, [3, 13, 13, 13, 13]]],
|
['backLegAccessories', 1, 27, [null, [3, 13, 13, 13, 13]]],
|
||||||
['chestAccessories', 1, 16, [null, [3, 5, 9], [3, 7, 5, 5, 7, 5, 7, 5, 7, 11, 9, 5, 11], [5, 7, 11], [5, 7]]],
|
['chestAccessories', 1, 16, [null, [3, 5, 9], [3, 7, 5, 5, 7, 5, 7, 5, 7, 11, 9, 5, 11], [5, 7, 11], [5, 7]]],
|
||||||
['backAccessories', 1, 16, [null, [3], [3, 7, 13, 5], [3, 7, 13, 5], [3, 9, 13, 5], [3, 3]]],
|
['backAccessories', 1, 16, [null, [3], [3, 7, 13, 5], [3, 7, 13, 5], [3, 9, 13, 5], [3, 3]]],
|
||||||
['waistAccessories', 1, 17, [null, [9], [9], [9]]],
|
['waistAccessories', 1, 17, [null, [9], [9], [9]]],
|
||||||
['earAccessories', -1, 1, [null, [3], [3], [3], [3], [5], [3, 11], [3], [3], [3], [3, 7, 7, 7], [3, 11, 11, 11], [3]]],
|
['earAccessories', -1, 1, [null, [3], [3], [3], [3], [5], [3, 11], [3], [3], [3], [3, 7, 7, 7], [3, 11, 11, 11], [3], []]],
|
||||||
['earAccessoriesBehind', -1, 1, [null, null, null, null, null, null, null, null, [3], null, [3, 7, 7, 7], [3, 11, 11, 11], []]],
|
['earAccessoriesBehind', -1, 1, [null, null, null, null, null, null, null, null, [3], null, [3, 7, 7, 7], [3, 11, 11, 11], [], []]],
|
||||||
['extraAccessories', -1, 1, [[11], [5], [5], [7], [9], [13], [5], [11], [9], [11, 11], [9], [9, 9], [7], [9], null, null, [13], [13]]],
|
['extraAccessories', -1, 1, [[11], [5], [5], [7], [9], [13], [5], [11], [9], [11, 11], [9], [9, 9], [7], [9], null, null, [13], [13]]],
|
||||||
['extraAccessoriesBehind', -1, 1, [[11], [5], null, [7], [9], null, null, null, null, null, null, null, [7], [9], [5], [5], [13], []]],
|
['extraAccessoriesBehind', -1, 1, [[11], [5], null, [7], [9], null, null, null, null, null, null, null, [7], [9], [5], [5], [13], []]],
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -114,6 +114,7 @@ describe('playerUtils', () => {
|
|||||||
account,
|
account,
|
||||||
character,
|
character,
|
||||||
ip: '',
|
ip: '',
|
||||||
|
isMobile: false,
|
||||||
map,
|
map,
|
||||||
isSwitchingMap: false,
|
isSwitchingMap: false,
|
||||||
pony,
|
pony,
|
||||||
@@ -132,7 +133,7 @@ describe('playerUtils', () => {
|
|||||||
safeX: 10,
|
safeX: 10,
|
||||||
safeY: 20,
|
safeY: 20,
|
||||||
lastPacket: 123,
|
lastPacket: 123,
|
||||||
lastBoopAction: 0,
|
lastBoopOrKissAction: 0,
|
||||||
lastExpressionAction: 0,
|
lastExpressionAction: 0,
|
||||||
lastX: 10,
|
lastX: 10,
|
||||||
lastY: 20,
|
lastY: 20,
|
||||||
@@ -591,7 +592,7 @@ describe('playerUtils', () => {
|
|||||||
|
|
||||||
boop(client, 100);
|
boop(client, 100);
|
||||||
|
|
||||||
expect(client.lastBoopOrKissAction).equal(100 + 500);
|
expect(client.lastBoopOrKissAction).equal(100 + 850);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('executes boop on found entity', () => {
|
it('executes boop on found entity', () => {
|
||||||
@@ -615,6 +616,7 @@ describe('playerUtils', () => {
|
|||||||
boop(client, 0);
|
boop(client, 0);
|
||||||
|
|
||||||
assert.notCalled(stubBoop);
|
assert.notCalled(stubBoop);
|
||||||
|
expect(client.pony.region!.entityUpdates.length).eql(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does nothing if cannot perform action', () => {
|
it('does nothing if cannot perform action', () => {
|
||||||
@@ -622,7 +624,7 @@ describe('playerUtils', () => {
|
|||||||
|
|
||||||
boop(client, 0);
|
boop(client, 0);
|
||||||
|
|
||||||
expect(client.pony.region!.entityUpdates).eql([]);
|
expect(client.pony.region!.entityUpdates.length).eql(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does nothing if moving', () => {
|
it('does nothing if moving', () => {
|
||||||
@@ -630,7 +632,7 @@ describe('playerUtils', () => {
|
|||||||
|
|
||||||
boop(client, 0);
|
boop(client, 0);
|
||||||
|
|
||||||
expect(client.pony.region!.entityUpdates).eql([]);
|
expect(client.pony.region!.entityUpdates.length).eql(0);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -663,6 +665,8 @@ describe('playerUtils', () => {
|
|||||||
|
|
||||||
it('updates entity flag to standing', () => {
|
it('updates entity flag to standing', () => {
|
||||||
client.pony.state = EntityState.PonySitting;
|
client.pony.state = EntityState.PonySitting;
|
||||||
|
client.lastBoopOrKissAction = 0;
|
||||||
|
client.lastExpressionAction = 0;
|
||||||
|
|
||||||
stand(client);
|
stand(client);
|
||||||
|
|
||||||
@@ -882,6 +886,8 @@ describe('playerUtils', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('sends given action', () => {
|
it('sends given action', () => {
|
||||||
|
client.lastExpressionAction = 0;
|
||||||
|
client.lastBoopOrKissAction = 0;
|
||||||
expressionAction(client, Action.Yawn);
|
expressionAction(client, Action.Yawn);
|
||||||
|
|
||||||
expect(client.pony.region!.entityUpdates).eql([
|
expect(client.pony.region!.entityUpdates).eql([
|
||||||
@@ -896,7 +902,7 @@ describe('playerUtils', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('does nothing if cannot perform action', () => {
|
it('does nothing if cannot perform action', () => {
|
||||||
client.lastExpressionAction = Date.now() + 1000;
|
client.lastExpressionAction = Date.now() + 2500;
|
||||||
|
|
||||||
expressionAction(client, Action.Yawn);
|
expressionAction(client, Action.Yawn);
|
||||||
|
|
||||||
@@ -913,6 +919,7 @@ describe('playerUtils', () => {
|
|||||||
|
|
||||||
it('updates last expression action', () => {
|
it('updates last expression action', () => {
|
||||||
client.lastExpressionAction = 0;
|
client.lastExpressionAction = 0;
|
||||||
|
client.lastBoopOrKissAction = 0;
|
||||||
|
|
||||||
expressionAction(client, Action.Yawn);
|
expressionAction(client, Action.Yawn);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user