Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 37 additions & 32 deletions src/game-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import { setWidgetPlugins } from '@server/world/actor/player/action/widget-actio
import { setItemPlugins } from '@server/world/actor/player/action/item-action';
import { setWorldItemPlugins } from '@server/world/actor/player/action/world-item-action';
import { setItemOnObjectPlugins } from '@server/world/actor/player/action/item-on-object-action';
import { setPlayerInitPlugins } from '@server/world/actor/player/player';
import { setNpcInitPlugins } from '@server/world/actor/npc/npc';

export let serverConfig: ServerConfig;
export let gameCache377: EarlyFormatGameCache;
Expand Down Expand Up @@ -47,6 +49,8 @@ export async function injectPlugins(): Promise<void> {
setWorldItemPlugins(actionTypes[ActionType.WORLD_ITEM_ACTION]);
setCommandPlugins(actionTypes[ActionType.COMMAND]);
setWidgetPlugins(actionTypes[ActionType.WIDGET_ACTION]);
setPlayerInitPlugins(actionTypes[ActionType.PLAYER_INIT]);
setNpcInitPlugins(actionTypes[ActionType.NPC_INIT]);
}

export function runGameServer(): void {
Expand All @@ -60,46 +64,47 @@ export function runGameServer(): void {
gameCache377 = new EarlyFormatGameCache('cache/377', { loadMaps: true, loadDefinitions: false, loadWidgets: false });
gameCache = new NewFormatGameCache('cache/435');
world = new World();
world.init();
injectPlugins();
injectPlugins().then(() => {
world.init();

if(process.argv.indexOf('-fakePlayers') !== -1) {
world.generateFakePlayers();
}

process.on('unhandledRejection', (err, promise) => {
if(err === 'WIDGET_CLOSED') {
return;
if(process.argv.indexOf('-fakePlayers') !== -1) {
world.generateFakePlayers();
}

console.error('Unhandled rejection (promise: ', promise, ', reason: ', err, ').');
});

net.createServer(socket => {
logger.info('Socket opened');
// socket.setNoDelay(true);
let clientConnection = new ClientConnection(socket);

socket.on('data', data => {
if(clientConnection) {
clientConnection.parseIncomingData(new RsBuffer(data));
process.on('unhandledRejection', (err, promise) => {
if(err === 'WIDGET_CLOSED') {
return;
}
});

socket.on('close', () => {
if(clientConnection) {
clientConnection.connectionDestroyed();
clientConnection = null;
}
console.error('Unhandled rejection (promise: ', promise, ', reason: ', err, ').');
});

socket.on('error', error => {
socket.destroy();
logger.error('Socket destroyed due to connection error.');
});
}).listen(serverConfig.port, serverConfig.host);
net.createServer(socket => {
logger.info('Socket opened');
// socket.setNoDelay(true);
let clientConnection = new ClientConnection(socket);

logger.info(`Game server listening on port ${serverConfig.port}.`);
socket.on('data', data => {
if(clientConnection) {
clientConnection.parseIncomingData(new RsBuffer(data));
}
});

socket.on('close', () => {
if(clientConnection) {
clientConnection.connectionDestroyed();
clientConnection = null;
}
});

socket.on('error', error => {
socket.destroy();
logger.error('Socket destroyed due to connection error.');
});
}).listen(serverConfig.port, serverConfig.host);

logger.info(`Game server listening on port ${serverConfig.port}.`);
});

const watcher = watch('dist/plugins/');
watcher.on('ready', function() {
Expand Down
11 changes: 11 additions & 0 deletions src/plugins/npcs/lumbridge/tramp-plugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { ActionType, RunePlugin } from '@server/plugins/plugin';
import { npcIds } from '@server/world/config/npc-ids';
import { npcInitAction } from '@server/world/actor/npc/npc';

const action: npcInitAction = (details) => {
setInterval(() => {
details.npc.updateFlags.addChatMessage({ message: `Welcome to RuneJS!` });
}, 10000);
};

export default new RunePlugin({ type: ActionType.NPC_INIT, npcIds: npcIds.tramp, action });
11 changes: 11 additions & 0 deletions src/plugins/player/login-unlock-emotes-plugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { ActionType, RunePlugin } from '@server/plugins/plugin';
import { playerInitAction } from '@server/world/actor/player/player';
import { unlockEmotes } from '@server/plugins/buttons/player-emotes-plugin';

export const action: playerInitAction = (details) => {
const { player } = details;

unlockEmotes(player);
};

export default new RunePlugin({ type: ActionType.PLAYER_INIT, action });
26 changes: 26 additions & 0 deletions src/plugins/player/login-update-settings-plugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { ActionType, RunePlugin } from '@server/plugins/plugin';
import { playerInitAction } from '@server/world/actor/player/player';
import { validateSettings } from '@server/world/actor/player/player-data';
import { widgetSettings } from '@server/world/config/widget';

export const action: playerInitAction = (details) => {
const { player } = details;

validateSettings(player);

const settings = player.settings;
player.outgoingPackets.updateClientConfig(widgetSettings.brightness, settings.screenBrightness);
player.outgoingPackets.updateClientConfig(widgetSettings.mouseButtons, settings.twoMouseButtonsEnabled ? 0 : 1);
player.outgoingPackets.updateClientConfig(widgetSettings.splitPrivateChat, settings.splitPrivateChatEnabled ? 1 : 0);
player.outgoingPackets.updateClientConfig(widgetSettings.chatEffects, settings.chatEffectsEnabled ? 0 : 1);
player.outgoingPackets.updateClientConfig(widgetSettings.acceptAid, settings.acceptAidEnabled ? 1 : 0);
player.outgoingPackets.updateClientConfig(widgetSettings.musicVolume, settings.musicVolume);
player.outgoingPackets.updateClientConfig(widgetSettings.soundEffectVolume, settings.soundEffectVolume);
player.outgoingPackets.updateClientConfig(widgetSettings.areaEffectVolume, settings.areaEffectVolume);
player.outgoingPackets.updateClientConfig(widgetSettings.runMode, settings.runEnabled ? 1 : 0);
player.outgoingPackets.updateClientConfig(widgetSettings.autoRetaliate, settings.autoRetaliateEnabled ? 0 : 1);
player.outgoingPackets.updateClientConfig(widgetSettings.attackStyle, settings.attackStyle);
player.outgoingPackets.updateClientConfig(widgetSettings.bankInsertMode, settings.bankInsertMode);
};

export default new RunePlugin({ type: ActionType.PLAYER_INIT, action });
12 changes: 8 additions & 4 deletions src/plugins/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import { WidgetActionPlugin } from '@server/world/actor/player/action/widget-act
import { ItemActionPlugin } from '@server/world/actor/player/action/item-action';
import { WorldItemActionPlugin } from '@server/world/actor/player/action/world-item-action';
import { ItemOnObjectActionPlugin } from '@server/world/actor/player/action/item-on-object-action';
import { PlayerInitPlugin } from '@server/world/actor/player/player';
import { NpcInitPlugin } from '@server/world/actor/npc/npc';

export enum ActionType {
BUTTON = 'button',
Expand All @@ -17,7 +19,9 @@ export enum ActionType {
NPC_ACTION = 'npc_action',
OBJECT_ACTION = 'object_action',
ITEM_ON_OBJECT_ACTION = 'item_on_object_action',
COMMAND = 'command'
COMMAND = 'command',
PLAYER_INIT = 'player_init',
NPC_INIT = 'npc_init'
}

export interface ActionPlugin {
Expand All @@ -27,12 +31,12 @@ export interface ActionPlugin {
export class RunePlugin {

public actions: (NpcActionPlugin | ObjectActionPlugin | ButtonActionPlugin | ItemOnItemActionPlugin | ItemOnObjectActionPlugin |
CommandActionPlugin | WidgetActionPlugin | ItemActionPlugin | WorldItemActionPlugin)[];
CommandActionPlugin | WidgetActionPlugin | ItemActionPlugin | WorldItemActionPlugin | PlayerInitPlugin | NpcInitPlugin)[];

public constructor(actions: NpcActionPlugin | ObjectActionPlugin | ButtonActionPlugin | ItemOnItemActionPlugin | ItemOnObjectActionPlugin |
CommandActionPlugin | WidgetActionPlugin | ItemActionPlugin | WorldItemActionPlugin |
CommandActionPlugin | WidgetActionPlugin | ItemActionPlugin | WorldItemActionPlugin | PlayerInitPlugin | NpcInitPlugin |
(NpcActionPlugin | ObjectActionPlugin | ButtonActionPlugin | ItemOnItemActionPlugin | ItemOnObjectActionPlugin |
CommandActionPlugin | WidgetActionPlugin | ItemActionPlugin | WorldItemActionPlugin)[]) {
CommandActionPlugin | WidgetActionPlugin | ItemActionPlugin | WorldItemActionPlugin | PlayerInitPlugin | NpcInitPlugin)[]) {
if (!Array.isArray(actions)) {
this.actions = [actions];
} else {
Expand Down
22 changes: 22 additions & 0 deletions src/world/actor/npc/npc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import { Position } from '@server/world/position';
import { world } from '@server/game-server';
import { Direction } from '@server/world/direction';
import { QuadtreeKey } from '@server/world/world';
import { ActionPlugin } from '@server/plugins/plugin';
import { basicNumberFilter } from '@server/plugins/plugin-loader';

interface NpcAnimations {
walk: number;
Expand All @@ -15,6 +17,19 @@ interface NpcAnimations {
turnLeft: number;
}

let npcInitPlugins: NpcInitPlugin[];

export type npcInitAction = (details: { npc: Npc }) => void;

export const setNpcInitPlugins = (plugins: ActionPlugin[]): void => {
npcInitPlugins = plugins as NpcInitPlugin[];
};

export interface NpcInitPlugin extends ActionPlugin {
action: npcInitAction;
npcIds: number | number[];
}

/**
* Represents a non-player character within the game world.
*/
Expand Down Expand Up @@ -54,6 +69,13 @@ export class Npc extends Actor {
public init(): void {
world.chunkManager.getChunkForWorldPosition(this.position).addNpc(this);
this.initiateRandomMovement();

new Promise(resolve => {
npcInitPlugins
.filter(plugin => basicNumberFilter(plugin.npcIds, this.id))
.forEach(plugin => plugin.action({ npc: this }));
resolve();
});
}

public async tick(): Promise<void> {
Expand Down
47 changes: 22 additions & 25 deletions src/world/actor/player/player.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ import {
defaultAppearance, defaultSettings,
loadPlayerSave,
PlayerSave, PlayerSettings,
savePlayerData, validateSettings
savePlayerData
} from './player-data';
import { ActiveWidget, widgets, widgetSettings } from '../../config/widget';
import { ActiveWidget, widgets } from '../../config/widget';
import { ContainerUpdateEvent, ItemContainer } from '../../items/item-container';
import { EquipmentBonuses, ItemDetails } from '../../config/item-data';
import { Item } from '../../items/item';
Expand All @@ -24,8 +24,8 @@ import { Chunk, ChunkUpdateItem } from '@server/world/map/chunk';
import { QuadtreeKey } from '@server/world/world';
import { daysSinceLastLogin } from '@server/util/time';
import { itemIds } from '@server/world/config/item-ids';
import { unlockEmotes } from '@server/plugins/buttons/player-emotes-plugin';
import { dialogueAction } from '@server/world/actor/player/action/dialogue-action';
import { ActionPlugin } from '@server/plugins/plugin';

const DEFAULT_TAB_WIDGET_IDS = [
92, widgets.skillsTab, 274, widgets.inventory.widgetId, widgets.equipment.widgetId, 271, 192, -1, 131, 148,
Expand All @@ -38,6 +38,18 @@ export enum Rights {
USER = 0
}

let playerInitPlugins: PlayerInitPlugin[];

export type playerInitAction = (details: { player: Player }) => void;

export const setPlayerInitPlugins = (plugins: ActionPlugin[]): void => {
playerInitPlugins = plugins as PlayerInitPlugin[];
};

export interface PlayerInitPlugin extends ActionPlugin {
action: playerInitAction;
}

/**
* A player character within the game world.
*/
Expand Down Expand Up @@ -209,8 +221,6 @@ export class Player extends Actor {
};
}

validateSettings(this);
this.updateWidgetSettings();
this.updateBonuses();
this.updateCarryWeight(true);

Expand All @@ -226,11 +236,14 @@ export class Player extends Actor {
this._loginDate = new Date();
this._lastAddress = (this._socket?.address() as AddressInfo)?.address || '127.0.0.1';

unlockEmotes(this);

this.outgoingPackets.flushQueue();
new Promise(resolve => {
playerInitPlugins.forEach(plugin => plugin.action({ player: this }));
resolve();
}).then(() => {
this.outgoingPackets.flushQueue();

logger.info(`${this.username}:${this.worldIndex} has logged in.`);
logger.info(`${this.username}:${this.worldIndex} has logged in.`);
});
}

public logout(): void {
Expand Down Expand Up @@ -484,22 +497,6 @@ export class Player extends Actor {
this.settings[config.setting] = config.value;
}

public updateWidgetSettings(): void {
const settings = this.settings;
this.outgoingPackets.updateClientConfig(widgetSettings.brightness, settings.screenBrightness);
this.outgoingPackets.updateClientConfig(widgetSettings.mouseButtons, settings.twoMouseButtonsEnabled ? 0 : 1);
this.outgoingPackets.updateClientConfig(widgetSettings.splitPrivateChat, settings.splitPrivateChatEnabled ? 1 : 0);
this.outgoingPackets.updateClientConfig(widgetSettings.chatEffects, settings.chatEffectsEnabled ? 0 : 1);
this.outgoingPackets.updateClientConfig(widgetSettings.acceptAid, settings.acceptAidEnabled ? 1 : 0);
this.outgoingPackets.updateClientConfig(widgetSettings.musicVolume, settings.musicVolume);
this.outgoingPackets.updateClientConfig(widgetSettings.soundEffectVolume, settings.soundEffectVolume);
this.outgoingPackets.updateClientConfig(widgetSettings.areaEffectVolume, settings.areaEffectVolume);
this.outgoingPackets.updateClientConfig(widgetSettings.runMode, settings.runEnabled ? 1 : 0);
this.outgoingPackets.updateClientConfig(widgetSettings.autoRetaliate, settings.autoRetaliateEnabled ? 0 : 1);
this.outgoingPackets.updateClientConfig(widgetSettings.attackStyle, settings.attackStyle);
this.outgoingPackets.updateClientConfig(widgetSettings.bankInsertMode, settings.bankInsertMode);
}

public updateBonuses(): void {
this.clearBonuses();

Expand Down