Browse Source

Merge branch 'master' into 'release'

Master

See merge request Isleward/isleward!584
tags/v0.10.6.10
Big Bad Waffle 2 years ago
parent
commit
3a21952a3b
35 changed files with 924 additions and 191 deletions
  1. +2
    -1
      src/client/ui/templates/death/death.js
  2. +2
    -2
      src/client/ui/templates/equipment/equipment.js
  3. +18
    -12
      src/client/ui/templates/inventory/inventory.js
  4. +3
    -1
      src/client/ui/templates/party/party.js
  5. +3
    -1
      src/client/ui/templates/quests/quests.js
  6. +3
    -1
      src/client/ui/templates/stash/stash.js
  7. +2
    -2
      src/client/ui/templates/wardrobe/wardrobe.js
  8. +3
    -3
      src/client/ui/templates/workbench/workbench.js
  9. +2
    -1
      src/server/clientComponents/gatherer.js
  10. +4
    -3
      src/server/clientComponents/keyboardMover.js
  11. +2
    -2
      src/server/clientComponents/pather.js
  12. +2
    -4
      src/server/clientComponents/spellbook.js
  13. +4
    -3
      src/server/clientComponents/touchMover.js
  14. +9
    -5
      src/server/components/door.js
  15. +10
    -10
      src/server/components/equipment.js
  16. +3
    -3
      src/server/components/extensions/socialCommands.js
  17. +4
    -2
      src/server/components/gatherer.js
  18. +26
    -26
      src/server/components/inventory.js
  19. +2
    -2
      src/server/components/inventory/getItem.js
  20. +2
    -2
      src/server/components/inventory/useItem.js
  21. +15
    -1
      src/server/components/player.js
  22. +2
    -2
      src/server/components/quests.js
  23. +1
    -1
      src/server/components/social.js
  24. +7
    -5
      src/server/components/stash.js
  25. +2
    -2
      src/server/components/trade.js
  26. +8
    -4
      src/server/components/wardrobe.js
  27. +8
    -4
      src/server/components/workbench.js
  28. +2
    -2
      src/server/components/workbench/craft.js
  29. +1
    -1
      src/server/config/maps/cave/dialogues.js
  30. +1
    -1
      src/server/config/quests/templates/questLootGen.js
  31. +12
    -18
      src/server/objects/objBase.js
  32. +116
    -1
      src/server/security/router.js
  33. +637
    -45
      src/server/security/routerConfig.js
  34. +5
    -17
      src/server/server/onConnection.js
  35. +1
    -1
      src/server/world/instancer.js

+ 2
- 1
src/client/ui/templates/death/death.js View File

@@ -36,7 +36,8 @@ define([
method: 'performAction',
data: {
cpn: 'stats',
method: 'respawn'
method: 'respawn',
data: {}
}
});
},


+ 2
- 2
src/client/ui/templates/equipment/equipment.js View File

@@ -172,7 +172,7 @@ define([
unequipItem: function (item) {
const isQuickslot = item.has('quickSlot');
const method = isQuickslot ? 'setQuickSlot' : 'unequip';
const data = isQuickslot ? { slot: item.quickSlot } : item.id;
const data = isQuickslot ? { slot: item.quickSlot } : { itemId: item.id };

client.request({
cpn: 'player',
@@ -290,7 +290,7 @@ define([

let cpn = 'equipment';
let method = 'equip';
let data = item.id;
let data = { itemId: item.id };

if (item.empty)
method = 'unequip';


+ 18
- 12
src/client/ui/templates/inventory/inventory.js View File

@@ -174,6 +174,8 @@ define([
let method = 'moveItem';

if ((this.hoverCell) && (this.hoverCell[0] !== this.dragItem[0])) {
const data = {};

let placeholder = $('<div></div>')
.insertAfter(this.dragItem);

@@ -182,10 +184,12 @@ define([
placeholder.remove();

let msgs = [{
id: this.dragItem.data('item').id,
pos: this.dragItem.index()
itemId: this.dragItem.data('item').id,
targetPos: this.dragItem.index()
}];

data.moveMsgs = msgs;

this.items.find(function (i) {
return (i.id === this.dragItem.data('item').id);
}, this).pos = this.dragItem.index();
@@ -194,19 +198,19 @@ define([
if (hoverCellItem) {
if ((hoverCellItem.name !== this.dragItem.data('item').name) || (!hoverCellItem.quantity)) {
msgs.push({
id: hoverCellItem.id,
pos: this.hoverCell.index()
itemId: hoverCellItem.id,
targetPos: this.hoverCell.index()
});

this.items.find(function (i) {
return (i.id === hoverCellItem.id);
}, this).pos = this.hoverCell.index();
} else {
delete data.moveMsgs;
data.fromId = this.dragItem.data('item').id;
data.toId = hoverCellItem.id;

method = 'combineStacks';
msgs = {
fromId: this.dragItem.data('item').id,
toId: hoverCellItem.id
};
}
}

@@ -216,7 +220,7 @@ define([
data: {
cpn: 'inventory',
method: method,
data: msgs
data
}
});

@@ -516,7 +520,9 @@ define([
else if ((action === 'learnAbility') && (!window.player.inventory.canEquipItem(item)))
return;

let data = item.id;
let data = {
itemId: item.id
};

let cpn = 'inventory';
if (['equip', 'setQuickSlot'].includes(action)) {
@@ -537,9 +543,9 @@ define([
cpn: 'player',
method: 'performAction',
data: {
cpn: cpn,
cpn,
method: action,
data: data
data
}
});
},


+ 3
- 1
src/client/ui/templates/party/party.js View File

@@ -216,7 +216,9 @@ define([
client.request({
cpn: 'social',
method: 'removeFromParty',
data: id
data: {
id
}
});
},



+ 3
- 1
src/client/ui/templates/quests/quests.js View File

@@ -86,7 +86,9 @@ define([
data: {
cpn: 'quests',
method: 'complete',
data: quest.id
data: {
questId: quest.id
}
}
});
},


+ 3
- 1
src/client/ui/templates/stash/stash.js View File

@@ -196,7 +196,9 @@ define([
data: {
cpn: 'stash',
method: 'withdraw',
data: item.id
data: {
itemId: item.id
}
}
});
},


+ 2
- 2
src/client/ui/templates/wardrobe/wardrobe.js View File

@@ -74,11 +74,11 @@ define([
cpn: 'player',
method: 'performAction',
data: {
targetId: this.wardrobeId,
cpn: 'wardrobe',
method: 'apply',
data: {
skinId: this.skin.id
skinId: this.skin.id,
targetId: this.wardrobeId
}
}
});


+ 3
- 3
src/client/ui/templates/workbench/workbench.js View File

@@ -86,10 +86,10 @@ define([
cpn: 'player',
method: 'performAction',
data: {
targetId: this.workbenchId,
cpn: 'workbench',
method: 'getRecipe',
data: {
targetId: this.workbenchId,
name: recipeName
}
},
@@ -215,10 +215,10 @@ define([
cpn: 'player',
method: 'performAction',
data: {
targetId: this.workbenchId,
cpn: 'workbench',
method: 'getRecipe',
data: {
targetId: this.workbenchId,
name: this.currentRecipe.name,
pickedItemIds
}
@@ -271,10 +271,10 @@ define([
cpn: 'player',
method: 'performAction',
data: {
targetId: this.workbenchId,
cpn: 'workbench',
method: 'craft',
data: {
targetId: this.workbenchId,
name: selectedRecipe,
pickedItemIds
}


+ 2
- 1
src/server/clientComponents/gatherer.js View File

@@ -61,7 +61,8 @@ define([
method: 'performAction',
data: {
cpn: 'gatherer',
method: 'gather'
method: 'gather',
data: {}
}
});
},


+ 4
- 3
src/server/clientComponents/keyboardMover.js View File

@@ -44,10 +44,11 @@ define([
if (keyEvent.key === 'esc') {
client.request({
cpn: 'player',
method: 'queueAction',
method: 'performAction',
data: {
action: 'clearQueue',
priority: true
cpn: 'player',
method: 'clearQueue',
data: {}
}
});
}


+ 2
- 2
src/server/clientComponents/pather.js View File

@@ -77,10 +77,10 @@ define([
client.request({
cpn: 'player',
method: 'move',
priority: !this.path.length,
data: {
x: x,
y: y
y: y,
priority: !this.path.length
}
});
},


+ 2
- 4
src/server/clientComponents/spellbook.js View File

@@ -102,9 +102,8 @@ define([
if (!target && this.target && (!this.hoverTarget || this.hoverTarget.id !== this.target.id)) {
client.request({
cpn: 'player',
method: 'queueAction',
method: 'castSpell',
data: {
action: 'spell',
priority: true,
target: null
}
@@ -193,9 +192,8 @@ define([

client.request({
cpn: 'player',
method: 'queueAction',
method: 'castSpell',
data: {
action: 'spell',
priority: input.isKeyDown('ctrl'),
spell: spell.id,
target: target,


+ 4
- 3
src/server/clientComponents/touchMover.js View File

@@ -87,10 +87,11 @@ define([

client.request({
cpn: 'player',
method: 'queueAction',
method: 'performAction',
data: {
action: 'clearQueue',
priority: true
cpn: 'player',
method: 'clearQueue',
data: {}
}
});



+ 9
- 5
src/server/components/door.js View File

@@ -80,9 +80,11 @@ module.exports = {
obj.syncer.setArray(true, 'serverActions', 'removeActions', {
key: 'u',
action: {
targetId: this.obj.id,
cpn: 'door',
method: 'unlock'
method: 'unlock',
data: {
targetId: this.obj.id
}
}
});
},
@@ -107,9 +109,11 @@ module.exports = {
key: 'u',
name: this.closed ? 'open door' : 'close door',
action: {
targetId: this.obj.id,
cpn: 'door',
method: 'unlock'
method: 'unlock',
data: {
targetId: this.obj.id
}
}
});
}
@@ -148,7 +152,7 @@ module.exports = {
const key = obj.inventory.items.find(i => i.keyId === this.key);

if (key && (key.singleUse || this.destroyKey)) {
obj.inventory.destroyItem(key.id, 1);
obj.inventory.destroyItem({ itemId: key.id }, 1);

const message = `The ${key.name} disintegrates on use`;
obj.social.notifySelf({ message });


+ 10
- 10
src/server/components/equipment.js View File

@@ -45,7 +45,7 @@ module.exports = {
}

if (!this.eq.has(item.slot)) {
this.equip(itemId);
this.equip({ itemId });
return true;
}
},
@@ -72,14 +72,14 @@ module.exports = {
slot = item.equipSlot || item.slot;
if (slot === 'twoHanded') {
if (this.eq.has('offHand'))
this.unequip(this.eq.offHand, true);
this.unequip({ itemId: this.eq.offHand }, true);

slot = 'oneHanded';
} else if (slot === 'offHand') {
if (this.eq.has('oneHanded')) {
let oneHandedEq = inventory.findItem(this.eq.oneHanded);
if (oneHandedEq.slot === 'twoHanded')
this.unequip(this.eq.oneHanded, true);
this.unequip({ itemId: this.eq.oneHanded }, true);
}
}

@@ -113,7 +113,7 @@ module.exports = {
if (this.eq[slot] === item.id)
return;

this.unequip(this.eq[slot], true);
this.unequip({ itemId: this.eq[slot] }, true);
}

applyItemStats(obj, item, true);
@@ -126,7 +126,7 @@ module.exports = {

if ((!obj.mob) || (item.ability)) {
if (item.spell)
inventory.learnAbility(itemId, item.runeSlot);
inventory.learnAbility({ itemId }, item.runeSlot);
else
obj.syncer.setArray(true, 'inventory', 'getItems', inventory.simplifyItem(item));
}
@@ -164,7 +164,7 @@ module.exports = {

if (item.spell) {
item.eq = true;
inventory.unlearnAbility(itemId, item.runeSlot);
inventory.unlearnAbility({ itemId }, item.runeSlot);
} else
obj.syncer.setArray(true, 'inventory', 'getItems', inventory.simplifyItem(item));

@@ -178,7 +178,7 @@ module.exports = {
unequipAll: function () {
let eq = this.eq;
Object.keys(this.eq).forEach(function (slot) {
this.unequip(eq[slot]);
this.unequip({ itemId: eq[slot] });
}, this);
},

@@ -228,7 +228,7 @@ module.exports = {
if (!item)
return;

inventory.useItem(this.quickSlots[0]);
inventory.useItem({ itemId: this.quickSlots[0] });

if (item.uses <= 0 && !item.quantity)
this.replaceQuickSlot(item);
@@ -256,7 +256,7 @@ module.exports = {

let errors = inventory.equipItemErrors(item);
if (errors.length > 0) {
this.unequip(itemId);
this.unequip({ itemId: itemId });

let message = ({
int: `You suddenly feel too stupid to wear your ${item.name}`,
@@ -290,7 +290,7 @@ module.exports = {
return;

if (findFaction.tier > tier) {
this.unequip(itemId);
this.unequip({ itemId });

const message = `You unequip your ${item.name} as it zaps you.`;
this.obj.social.notifySelf({


+ 3
- 3
src/server/components/extensions/socialCommands.js View File

@@ -352,7 +352,7 @@ module.exports = {
unEq: function () {
let eq = this.obj.equipment;
Object.keys(eq.eq).forEach(function (slot) {
eq.unequip(eq.eq[slot]);
eq.unequip({ itemId: eq.eq[slot] });
});
},

@@ -362,7 +362,7 @@ module.exports = {
inventory.items
.filter(i => !i.eq)
.map(i => i.id)
.forEach(i => inventory.destroyItem(i, null, true));
.forEach(i => inventory.destroyItem({ itemId: i }, null, true));
},

getItem: function (config) {
@@ -442,7 +442,7 @@ module.exports = {
let newItem = this.obj.inventory.getItem(item);

if (eq)
this.obj.equipment.equip(newItem.id);
this.obj.equipment.equip({ itemId: newItem.id });
},

getGold: function (amount) {


+ 4
- 2
src/server/components/gatherer.js View File

@@ -271,9 +271,11 @@ module.exports = {
obj.syncer.setArray(true, 'serverActions', action, {
key: 'u',
action: {
targetId: obj.id,
cpn: 'gatherer',
method: 'gather'
method: 'gather',
data: {
targetId: obj.id
}
}
});
},


+ 26
- 26
src/server/components/inventory.js View File

@@ -247,10 +247,10 @@ module.exports = {

toItem.quantity += fromItem.quantity;
this.obj.syncer.setArray(true, 'inventory', 'getItems', toItem);
this.destroyItem(fromItem.id, null, true);
this.destroyItem({ itemId: fromItem.id }, null, true);
},

useItem: function (itemId) {
useItem: function ({ itemId }) {
useItem(this, itemId);
},

@@ -275,8 +275,8 @@ module.exports = {
this.obj.syncer.setArray(true, 'inventory', 'getItems', item);
},

stashItem: async function (id) {
const item = this.findItem(id);
stashItem: async function ({ itemId }) {
const item = this.findItem(itemId);
if (!item || item.quest || item.noStash)
return;

@@ -288,11 +288,11 @@ module.exports = {
if (!success)
return;

this.destroyItem(id, null, true);
this.destroyItem({ itemId: itemId }, null, true);
},

salvageItem: function (id) {
let item = this.findItem(id);
salvageItem: function ({ itemId }) {
let item = this.findItem(itemId);
if ((!item) || (item.material) || (item.quest) || (item.noSalvage) || (item.eq))
return;
@@ -300,7 +300,7 @@ module.exports = {
let items = salvager.salvage(item);
this.destroyItem(id);
this.destroyItem({ itemId: itemId });
for (const material of items) {
this.getItem(material, true, false, false, true);
@@ -314,27 +314,27 @@ module.exports = {
this.obj.social.notifySelfArray(messages);
},

destroyItem: function (id, amount, force) {
let item = this.findItem(id);
destroyItem: function ({ itemId }, amount, force) {
let item = this.findItem(itemId);
if (!item || (item.noDestroy && !force))
return;

amount = amount || item.quantity;

if (item.eq)
this.obj.equipment.unequip(id);
this.obj.equipment.unequip({ itemId });

if ((item.quantity) && (amount)) {
item.quantity -= amount;
if (item.quantity <= 0) {
this.items.spliceWhere(i => i.id === id);
this.obj.syncer.setArray(true, 'inventory', 'destroyItems', id);
this.items.spliceWhere(i => i.id === itemId);
this.obj.syncer.setArray(true, 'inventory', 'destroyItems', itemId);
} else
this.obj.syncer.setArray(true, 'inventory', 'getItems', item);
} else {
this.items.spliceWhere(i => i.id === id);
this.obj.syncer.setArray(true, 'inventory', 'destroyItems', id);
this.obj.syncer.deleteFromArray(true, 'inventory', 'getItems', i => i.id === id);
this.items.spliceWhere(i => i.id === itemId);
this.obj.syncer.setArray(true, 'inventory', 'destroyItems', itemId);
this.obj.syncer.deleteFromArray(true, 'inventory', 'getItems', i => i.id === itemId);
}

this.obj.fireEvent('afterDestroyItem', item, amount);
@@ -343,8 +343,8 @@ module.exports = {
return item;
},

dropItem: function (id) {
let item = this.findItem(id);
dropItem: function ({ itemId }) {
let item = this.findItem(itemId);
if ((!item) || (item.noDrop) || (item.quest))
return;

@@ -367,25 +367,25 @@ module.exports = {
return;

if (item.eq)
this.obj.equipment.unequip(id);
this.obj.equipment.unequip(itemId);

this.items.spliceWhere(i => i.id === id);
this.items.spliceWhere(i => i.id === itemId);

this.obj.syncer.setArray(true, 'inventory', 'destroyItems', id);
this.obj.syncer.setArray(true, 'inventory', 'destroyItems', itemId);

this.createBag(dropCell.x, dropCell.y, [item]);

events.emit('afterPlayerDropItem', this.obj, item);
},

moveItem: function (msgs) {
msgs.forEach(function (m) {
let item = this.findItem(m.id);
moveItem: function ({ moveMsgs }) {
moveMsgs.forEach(({ itemId, targetPos }) => {
let item = this.findItem(itemId);
if (!item)
return;

item.pos = m.pos;
}, this);
item.pos = targetPos;
});
},

hookItemEvents: function (items) {


+ 2
- 2
src/server/components/inventory/getItem.js View File

@@ -124,10 +124,10 @@ module.exports = (cpnInv, item, hideMessage, noStack, hideAlert, createBagIfFull

if (item.eq) {
if (item.ability)
cpnInv.learnAbility(item.id, item.runeSlot);
cpnInv.learnAbility({ itemId: item.id }, item.runeSlot);
else {
delete item.eq;
obj.equipment.equip(item.id);
obj.equipment.equip({ itemId: item.id });
}
} else if (item.has('quickSlot')) {
obj.equipment.setQuickSlot({


+ 2
- 2
src/server/components/inventory/useItem.js View File

@@ -67,7 +67,7 @@ module.exports = async (cpnInv, itemId) => {
if (item.recipe) {
const didLearn = await learnRecipe(obj, item);
if (didLearn)
cpnInv.destroyItem(itemId, 1);
cpnInv.destroyItem({ itemId }, 1);

return;
}
@@ -107,7 +107,7 @@ module.exports = async (cpnInv, itemId) => {
}
}

cpnInv.destroyItem(itemId, 1);
cpnInv.destroyItem({ itemId }, 1);
if (item.has('quickSlot'))
cpnInv.obj.equipment.replaceQuickSlot(item);
}


+ 15
- 1
src/server/components/player.js View File

@@ -260,7 +260,13 @@ module.exports = {
move: function (msg) {
atlas.queueAction(this.obj, {
action: 'move',
priority: msg.priority,
data: msg.data
});
},

castSpell: function (msg) {
atlas.queueAction(this.obj, {
action: 'spell',
data: msg.data
});
},
@@ -274,5 +280,13 @@ module.exports = {
msg.data.data.callbackId = atlas.registerCallback(msg.callback);

atlas.performAction(this.obj, msg.data);
},

clearQueue: function (msg) {
const spellbook = this.obj.spellbook;
if (spellbook.isCasting())
spellbook.stopCasting();
else
this.obj.clearQueue();
}
};

+ 2
- 2
src/server/components/quests.js View File

@@ -35,8 +35,8 @@ module.exports = {
return true;
},

complete: function (id) {
let quest = this.quests.find(q => q.id === id);
complete: function ({ questId }) {
let quest = this.quests.find(q => q.id === questId);
if ((!quest) || (!quest.isReady))
return;



+ 1
- 1
src/server/components/social.js View File

@@ -222,7 +222,7 @@ module.exports = {
return;
}

let target = cons.players.find(c => c.id === msg.data);
let target = cons.players.find(c => c.id === msg.data.id);
if (!target)
return;



+ 7
- 5
src/server/components/stash.js View File

@@ -101,13 +101,13 @@ module.exports = {
return true;
},

withdraw: function (id) {
withdraw: function ({ itemId }) {
const { active, items, obj } = this;

if (!active)
return;

let item = items.find(i => i.id === id);
let item = items.find(i => i.id === itemId);
if (!item)
return;
else if (!obj.inventory.hasSpace(item)) {
@@ -122,7 +122,7 @@ module.exports = {
obj.inventory.getItem(item);
items.spliceWhere(i => i === item);

obj.instance.syncer.queue('onRemoveStashItems', [id], [obj.serverId]);
obj.instance.syncer.queue('onRemoveStashItems', [itemId], [obj.serverId]);
},

setActive: function (active) {
@@ -135,9 +135,11 @@ module.exports = {
id: 'openStash',
key: 'u',
action: {
targetId: obj.id,
cpn: 'stash',
method: 'open'
method: 'open',
data: {
targetId: obj.id
}
}
});



+ 2
- 2
src/server/components/trade.js View File

@@ -240,7 +240,7 @@ module.exports = {

if (item.worth.currency) {
let currencyItem = this.obj.inventory.items.find(i => (i.name === item.worth.currency));
this.obj.inventory.destroyItem(currencyItem.id, item.worth.amount, true);
this.obj.inventory.destroyItem({ itemId: currencyItem.id }, item.worth.amount, true);
} else {
targetTrade.gold += ~~(item.worth * markup);
this.gold -= ~~(item.worth * markup);
@@ -270,7 +270,7 @@ module.exports = {
return;

const oldQuantity = item.quantity;
this.obj.inventory.destroyItem(msg.itemId);
this.obj.inventory.destroyItem({ itemId: msg.itemId });

if (oldQuantity)
item.quantity = oldQuantity;


+ 8
- 4
src/server/components/wardrobe.js View File

@@ -42,9 +42,11 @@ module.exports = {
obj.syncer.setArray(true, 'serverActions', 'removeActions', {
key: 'u',
action: {
targetId: this.obj.id,
cpn: 'wardrobe',
method: 'access'
method: 'access',
data: {
targetId: this.obj.id
}
}
});

@@ -64,9 +66,11 @@ module.exports = {
key: 'u',
name: 'open wardrobe',
action: {
targetId: this.obj.id,
cpn: 'wardrobe',
method: 'open'
method: 'open',
data: {
targetId: this.obj.id
}
}
});



+ 8
- 4
src/server/components/workbench.js View File

@@ -47,9 +47,11 @@ module.exports = {
obj.syncer.setArray(true, 'serverActions', 'removeActions', {
key: 'u',
action: {
targetId: this.obj.id,
cpn: 'workbench',
method: 'access'
method: 'open',
data: {
targetId: this.obj.id
}
}
});

@@ -66,9 +68,11 @@ module.exports = {
key: 'u',
name: 'access workbench',
action: {
targetId: this.obj.id,
cpn: 'workbench',
method: 'open'
method: 'open',
data: {
targetId: this.obj.id
}
}
});



+ 2
- 2
src/server/components/workbench/craft.js View File

@@ -33,7 +33,7 @@ module.exports = (cpnWorkbench, msg) => {
if (!canCraft)
return null;

materials.forEach(m => inventory.destroyItem(m.id, m.needQuantity));
materials.forEach(m => inventory.destroyItem({ itemId: m.id }, m.needQuantity));

let resultMsg = null;

@@ -56,7 +56,7 @@ module.exports = (cpnWorkbench, msg) => {
applyItemStats(crafter, p, true);

if (p.slot !== oldSlots[i])
equipment.unequip(p.id);
equipment.unequip({ itemId: p.id });

spellbook.calcDps();



+ 1
- 1
src/server/config/maps/cave/dialogues.js View File

@@ -231,7 +231,7 @@ module.exports = {
return;
obj.reputation.getReputation('akarei', (crystals.quantity || 1) * 15);

inventory.destroyItem(crystals.id);
inventory.destroyItem({ itemId: crystals.id });
}
}
}


+ 1
- 1
src/server/config/quests/templates/questLootGen.js View File

@@ -76,7 +76,7 @@ module.exports = {
let inventory = this.obj.inventory;
let item = inventory.items.find((i => i.name === this.item.name).bind(this));
if (item)
this.obj.inventory.destroyItem(item.id, this.need);
this.obj.inventory.destroyItem({ itemId: item.id }, this.need);
},

events: {


+ 12
- 18
src/server/objects/objBase.js View File

@@ -176,22 +176,16 @@ module.exports = {
});
},

queue: function (action) {
if (action.action === 'clearQueue') {
let spellbook = this.spellbook;
if (spellbook.isCasting())
spellbook.stopCasting();
else
this.clearQueue();
return;
} else if (action.action === 'spell') {
queue: function (msg) {
const { action, auto, data: { priority } } = msg;

if (action === 'spell') {
let spellbook = this.spellbook;
const isCasting = spellbook.isCasting();

if (isCasting && (!action.priority || !spellbook.canCast(action))) {
if (action.auto)
spellbook.queueAuto(action);
if (isCasting && (!priority || !spellbook.canCast(msg))) {
if (auto)
spellbook.queueAuto(msg);

return;
}
@@ -200,15 +194,15 @@ module.exports = {
spellbook.stopCasting();

this.actionQueue.spliceWhere(a => a.priority);
this.actionQueue.splice(0, 0, action);
this.actionQueue.splice(0, 0, msg);
} else {
if (action.priority) {
if (priority) {
this.spellbook.stopCasting();
this.actionQueue.splice(0, 0, action);
this.actionQueue.splice(0, 0, msg);
return;
}

this.actionQueue.push(action);
this.actionQueue.push(msg);
}
},

@@ -270,7 +264,7 @@ module.exports = {
if (!success)
this.clearQueue();
} else if (q.action === 'spell') {
let success = this.spellbook.cast(q);
let success = this.spellbook.cast(q.data);
if (!success)
this.performQueue();
}


+ 116
- 1
src/server/security/router.js View File

@@ -1,4 +1,4 @@
const { routerConfig: { allowed, allowTargetId, secondaryAllowed, globalAllowed, secondaryAllowTargetId } } = require('./routerConfig');
const { routerConfig: { signatures, allowed, allowTargetId, secondaryAllowed, globalAllowed, secondaryAllowTargetId } } = require('./routerConfig');

module.exports = {
allowedCpn: function (msg) {
@@ -41,5 +41,120 @@ module.exports = {
const result = globalAllowed[threadModule] && globalAllowed[threadModule].includes(method);

return result;
},

keysCorrect: function (obj, keys) {
const foundIncorrect = keys.some(({ key, dataType, optional, spec }) => {
if (!obj.hasOwnProperty(key)) {
if (optional)
return false;

return true;
}

const value = obj[key];

if (dataType === 'string' || dataType === 'boolean')
return dataType !== typeof(value);
else if (dataType === 'numberOrString')
return (typeof(value) !== 'string' && !Number.isFinite(value));
else if (dataType === 'integerOrString')
return (typeof(value) !== 'string' && !Number.isInteger(value));
else if (dataType === 'integer')
return !Number.isInteger(value);
else if (dataType === 'integerNullOrPosition')
return !Number.isInteger(value) && value !== null && (typeof(value) !== 'object' && value.hasOwnProperty('x') && value.hasOwnProperty('y'));
else if (dataType === 'arrayOfStrings')
return (!Array.isArray(value) || value.some(v => typeof(v) !== 'string'));
else if (dataType === 'arrayOfIntegers')
return (!Array.isArray(value) || value.some(v => !Number.isInteger(v)));
else if (dataType === 'arrayOfObjects') {
if (!Array.isArray(value) || value.some(v => v === null || typeof(v) !== 'object'))
return true;

const foundIncorrectObject = value.some(v => !this.keysCorrect(v, spec));
if (foundIncorrectObject)
return true;

return foundIncorrectObject;
} else if (dataType === 'object') {
if (typeof(value) !== 'object' || value === null)
return true;

if (!spec)
return false;

const foundIncorrectObject = !this.keysCorrect(value, spec);
if (foundIncorrectObject)
return true;

return foundIncorrectObject;
} else if (dataType === 'stringOrNull')
return (typeof(value) !== 'string' && value !== null);
else if (dataType === 'mixed')
return false;

return true;
});

if (foundIncorrect)
return false;

const foundInvalid = Object.keys(obj).some(o => !keys.some(k => k.key === o));

return !foundInvalid;
},

signatureCorrect: function (msg, config) {
if (config.callback !== 'deferred') {
if (config.callback === true && !msg.callback)
return false;
else if (config.callback === false && !!msg.callback)
return false;
}

const expectKeys = config.data;

const keysCorrect = this.keysCorrect(msg.data, expectKeys);

return keysCorrect;
},

isMsgValid: function (msg) {
let signature;

if (msg.module) {
if (msg.threadModule !== undefined || msg.cpn !== undefined || msg.data.cpn !== undefined)
return false;

signature = signatures.global[msg.module]?.[msg.method];
} else if (msg.threadModule) {
if (msg.module !== undefined || msg.cpn !== undefined || msg.data.cpn !== undefined)
return false;

signature = signatures.threadGlobal[msg.threadModule]?.[msg.method];
} else if (msg.cpn) {
if (msg.module !== undefined || msg.threadModule !== undefined)
return false;

signature = signatures.cpnMethods[msg.cpn]?.[msg.method];
}

if (!signature)
return false;

const result = this.signatureCorrect(msg, signature);

if (!result || msg.cpn !== 'player' || msg.method !== 'performAction')
return result;

const signatureThreadMsg = signatures.threadCpnMethods[msg.data.cpn]?.[msg.data.method];

if (!signatureThreadMsg)
return false;

const resultSub = this.signatureCorrect(msg.data, signatureThreadMsg);

return resultSub;
}
};

+ 637
- 45
src/server/security/routerConfig.js View File

@@ -1,50 +1,636 @@
let events = require('../misc/events');

const routerConfig = {
//Component methods that can be called on the main thread
allowed: {
player: ['performAction', 'queueAction', 'move'],
auth: ['login', 'register', 'play', 'getCharacterList', 'getCharacter', 'deleteCharacter', 'getSkinList', 'createCharacter', 'getCustomChannels'],
social: ['chat', 'getInvite', 'acceptInvite', 'declineInvite', 'removeFromParty', 'leaveParty']
},
//Component methods that can be called with a targetId
// which means that we're not calling our own component method but instead, another object's component method
allowTargetId: {
social: ['getInvite', 'acceptInvite', 'declineInvite']
},
//Component methods that can be called on map threads through `performAction` or `queueAction` methods
secondaryAllowed: {
dialogue: ['talk'],
gatherer: ['gather'],
quests: ['complete'],
inventory: ['combineStacks', 'splitStack', 'useItem', 'moveItem', 'learnAbility', 'unlearnAbility', 'dropItem', 'destroyItem', 'salvageItem', 'stashItem', 'sortInventory'],
equipment: ['equip', 'unequip', 'setQuickSlot', 'useQuickSlot', 'inspect'],
stash: ['withdraw', 'open'],
trade: ['buySell'],
door: ['lock', 'unlock'],
wardrobe: ['open', 'apply'],
stats: ['respawn'],
passives: ['tickNode', 'untickNode'],
workbench: ['open', 'craft', 'getRecipe']
},
//Component methods that can be called on map threads with a targetId
// which means that we're not calling our own component method but instead, another object's component method
// These are called through `performAction` or `queueAction` methods
secondaryAllowTargetId: {
door: ['lock', 'unlock'],
gatherer: ['gather'],
equipment: ['inspect'],
stash: ['open'],
wardrobe: ['open', 'apply'],
workbench: ['open', 'craft', 'getRecipe']
},
//Global module methods that can be called on the main thread or map threads
globalAllowed: {
clientConfig: ['getClientConfig'],
leaderboard: ['requestList'],
cons: ['unzone'],
rezoneManager: ['clientAck'],
instancer: ['clientAck']
signatures: {
global: {
clientConfig: {
getClientConfig: {
callback: true,
data: []
}
},
leaderboard: {
requestList: {
callback: true,
data: [
{
key: 'offset',
dataType: 'integer'
},
{
key: 'prophecies',
dataType: 'arrayOfStrings'
}
]
}
},
cons: {
unzone: {
callback: true,
data: []
}
}
},
threadGlobal: {
instancer: {
clientAck: {
callback: false,
data: []
}
},
rezoneManager: {
clientAck: {
callback: false,
data: []
}
}
},
cpnMethods: {
auth: {
login: {
callback: true,
data: [
{
key: 'username',
dataType: 'string'
},
{
key: 'password',
dataType: 'string'
}
]
},
register: {
callback: true,
data: [
{
key: 'username',
dataType: 'string'
},
{
key: 'password',
dataType: 'string'
}
]
},
deleteCharacter: {
callback: true,
data: [
{
key: 'name',
dataType: 'string'
}
]
},
getSkinList: {
callback: true,
data: []
},
createCharacter: {
callback: true,
data: [
{
key: 'name',
dataType: 'string'
},
{
key: 'class',
dataType: 'string'
},
{
key: 'skinId',
dataType: 'string'
},
{
key: 'prophecies',
dataType: 'arrayOfStrings'
}
]
},
getCharacterList: {
callback: true,
data: []
},
getCharacter: {
callback: true,
data: [
{
key: 'name',
dataType: 'string'
}
]
},
play: {
callback: true,
data: [
{
key: 'name',
dataType: 'string'
}
]
}
},
player: {
move: {
callback: false,
data: [
{
key: 'x',
dataType: 'integer'
},
{
key: 'y',
dataType: 'integer'
},
{
key: 'priority',
dataType: 'boolean',
optional: true
}
]
},
castSpell: {
callback: false,
data: [
{
key: 'priority',
dataType: 'boolean'
},
{
key: 'target',
dataType: 'integerNullOrPosition'
},
{
key: 'spell',
dataType: 'integer'
},
{
key: 'self',
dataType: 'boolean',
optional: true
}
]
},
performAction: {
callback: 'deferred',
data: [
{
key: 'cpn',
dataType: 'string'
},
{
key: 'method',
dataType: 'string'
},
{
key: 'data',
dataType: 'object'
}
]
}
},
social: {
chat: {
callback: false,
data: [
{
key: 'message',
dataType: 'string'
},
{
key: 'type',
dataType: 'string',
optional: true
},
{
key: 'subType',
dataType: 'stringOrNull',
optional: true
},
{
key: 'item',
dataType: 'object',
optional: true
}
]
},
getInvite: {
callback: false,
data: [
{
key: 'targetId',
dataType: 'integer'
}
]
},
acceptInvite: {
callback: false,
data: [
{
key: 'targetId',
dataType: 'integer'
}
]
},
declineInvite: {
callback: false,
data: [
{
key: 'targetId',
dataType: 'integer'
}
]
},
removeFromParty: {
callback: false,
data: [
{
key: 'id',
dataType: 'integer'
}
]
},
leaveParty: {
callback: false,
data: []
}

}
},
threadCpnMethods: {
dialogue: {
talk: {
callback: false,
data: [
{
key: 'target',
dataType: 'integerOrString'
},
{
key: 'state',
dataType: 'numberOrString'
}
]
}
},
gatherer: {
gather: {
callback: false,
data: [
{
key: 'targetId',
dataType: 'integer'
}
]
}
},
quests: {
complete: {
callback: false,
data: [
{
key: 'questId',
dataType: 'integer'
}
]
}
},
player: {
clearQueue: {
callback: false,
data: []
}
},
inventory: {
combineStacks: {
callback: false,
data: [
{
key: 'fromId',
dataType: 'integer'
},
{
key: 'toId',
dataType: 'integer'
}
]
},
splitStack: {
callback: false,
data: [
{
key: 'itemId',
dataType: 'integer'
},
{
key: 'stackSize',
dataType: 'integer'
}
]
},
useItem: {
callback: false,
data: [
{
key: 'itemId',
dataType: 'integer'
}
]
},
moveItem: {
callback: false,
data: [
{
key: 'moveMsgs',
dataType: 'arrayOfObjects',
spec: [
{
key: 'itemId',
dataType: 'integer'
},
{
key: 'targetPos',
dataType: 'integer'
}
]
}
]
},
learnAbility: {
callback: false,
data: [
{
key: 'itemId',
dataType: 'integer'
},
{
key: 'slot',
dataType: 'integer',
optional: true
}
]
},
unlearnAbility: {
callback: false,
data: [
{
key: 'itemId',
dataType: 'integer'
}
]
},
dropItem: {
callback: false,
data: [
{
key: 'itemId',
dataType: 'integer'
}
]
},
destroyItem: {
callback: false,
data: [
{
key: 'itemId',
dataType: 'integer'
}
]
},
salvageItem: {
callback: false,
data: [
{
key: 'itemId',
dataType: 'integer'
}
]
},
stashItem: {
callback: false,
data: [
{
key: 'itemId',
dataType: 'integer'
}
]
},
sortInventory: {
callback: false,
data: []
}
},
equipment: {
equip: {
callback: false,
data: [
{
key: 'itemId',
dataType: 'integer'
},
{
key: 'slot',
dataType: 'string',
optional: true
}
]
},
unequip: {
callback: false,
data: [
{
key: 'itemId',
dataType: 'integer'
},
{
key: 'slot',
dataType: 'string',
optional: true
}
]
},
setQuickSlot: {
data: [
{
key: 'itemId',
dataType: 'integer'
},
{
key: 'slot',
dataType: 'integer'
}
]
},
useQuickSlot: {
data: [
{
key: 'slot',
dataType: 'integer'
}
]
},
inspect: {
callback: false,
data: [
{
key: 'playerId',
dataType: 'integer'
}
]
}
},
stash: {
withdraw: {
callback: false,
data: [
{
key: 'itemId',
dataType: 'integer'
}
]
},
open: {
callback: false,
data: [
{
key: 'targetId',
dataType: 'integer'
}
]
}
},
trade: {
buySell: {
callback: false,
data: [
{
key: 'itemId',
dataType: 'integer'
},
{
key: 'action',
dataType: 'string'
}
]
}
},
door: {
lock: {
callback: false,
data: [
{
key: 'targetId',
dataType: 'integer'
}
]
},
unlock: {
callback: false,
data: [
{
key: 'targetId',
dataType: 'integer'
}
]
}
},
wardrobe: {
open: {
callback: false,
data: [
{
key: 'targetId',
dataType: 'integer'
}
]
},
apply: {
callback: false,
data: [
{
key: 'targetId',
dataType: 'integer'
},
{
key: 'skinId',
dataType: 'string'
}
]
}
},
stats: {
respawn: {
callback: false,
data: []
}
},
passives: {
tickNode: {
callback: false,
data: [
{
key: 'nodeId',
dataType: 'integer'
}
]
},
untickNode: {
callback: false,
data: [
{
key: 'nodeId',
dataType: 'integer',
optional: true
}
]
}
},
workbench: {
open: {
callback: false,
data: [
{
key: 'targetId',
dataType: 'integer'
}
]
},
craft: {
callback: false,
data: [
{
key: 'targetId',
dataType: 'integer'
},
{
key: 'name',
dataType: 'string'
},
{
key: 'pickedItemIds',
dataType: 'arrayOfIntegers'
}
]
},
getRecipe: {
data: [
{
key: 'targetId',
dataType: 'integer'
},
{
key: 'name',
dataType: 'string'
},
{
key: 'pickedItemIds',
dataType: 'arrayOfIntegers',
optional: true
}
]
}
}
}
}
};

@@ -52,6 +638,12 @@ module.exports = {
routerConfig,

init: function () {
events.emit('onBeforeGetRouterConfig', routerConfig);
routerConfig.allowed = {};
routerConfig.allowTargetId = {};
routerConfig.secondaryAllowed = {};
routerConfig.secondaryAllowTargetId = {};
routerConfig.globalAllowed = {};

events.emit('onBeforeGetRouterSignatureConfig', routerConfig);
}
};

+ 5
- 17
src/server/server/onConnection.js View File

@@ -16,26 +16,14 @@ const onRequest = (socket, msg, callback) => {
if (!msg.data)
msg.data = {};

if (msg.cpn) {
if (!router.allowedCpn(msg))
return;

delete msg.threadModule;
delete msg.module;
if (!router.isMsgValid(msg))
return;

if (msg.cpn)
cons.route(socket, msg);
} else if (msg.threadModule) {
if (!router.allowedGlobalCall(msg.threadModule, msg.method))
return;

delete msg.cpn;
delete msg.module;

else if (msg.threadModule)
cons.route(socket, msg);
} else {
if (!router.allowedGlobal(msg))
return;

else {
const source = cons.players.find(p => p.socket.id === socket.id);

msg.socket = socket;


+ 1
- 1
src/server/world/instancer.js View File

@@ -285,7 +285,7 @@ module.exports = {

performAction: function (msg) {
let obj = null;
let targetId = msg.action.targetId;
let targetId = msg.action.data.targetId;
if (!targetId)
obj = objects.find(o => o.serverId === msg.id);
else {


Loading…
Cancel
Save