Initial commit
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
const Game = require("../Game/Game");
|
||||
const { players } = Game;
|
||||
|
||||
const extractBehaviourFeatures = require("../Utility/extractBehaviourFeatures");
|
||||
|
||||
// const NORMAL_CLOSE_CODES = [0, 1, 2];
|
||||
|
||||
module.exports = function (ws, code, message) {
|
||||
if (code === 1337) return;
|
||||
|
||||
const player = players[ws.accountID];
|
||||
if (!player) {
|
||||
ws.isInterrupted = true;
|
||||
delete socket.sockets[ws.id];
|
||||
|
||||
return;
|
||||
};
|
||||
|
||||
const playerActions = global.actions[player.id];
|
||||
const features = extractBehaviourFeatures(playerActions);
|
||||
|
||||
delete global.actions[player.id];
|
||||
|
||||
if (features) {
|
||||
features.playerID = player.id;
|
||||
features.timestampStart = new Date(ws.connectedAt);
|
||||
|
||||
global.StatisticsWorker.postMessage({
|
||||
id: 2,
|
||||
data: features
|
||||
});
|
||||
};
|
||||
|
||||
player.inDistance = {};
|
||||
player.isAvailable = false;
|
||||
player.hasUnexpectedlyLeft = true;
|
||||
|
||||
const individualPackets = player.map.networkPacketsInvidiual[player.id];
|
||||
if (individualPackets) individualPackets.length = 0;
|
||||
console.info("close event", performance.now());
|
||||
delete socket.sockets[ws.id];
|
||||
};
|
||||
@@ -0,0 +1,3 @@
|
||||
module.exports = function (ws) {
|
||||
console.info("Drain?");
|
||||
};
|
||||
@@ -0,0 +1,94 @@
|
||||
const packetTable = require("../Data/packets");
|
||||
const { players } = require("../Game/Game");
|
||||
|
||||
const actions = [];
|
||||
global.actions = actions;
|
||||
|
||||
const { MongoClient } = require("mongodb");
|
||||
let db = null;
|
||||
|
||||
async function connect() {
|
||||
const client = new MongoClient("mongodb://localhost:27017");
|
||||
await client.connect({
|
||||
maxPoolSize: 100,
|
||||
minPoolSize: 20,
|
||||
maxConnecting: 20,
|
||||
waitQueueTimeoutMS: 0,
|
||||
socketTimeoutMS: 30000,
|
||||
connectTimeoutMS: 10000,
|
||||
retryWrites: false,
|
||||
directConnection: true
|
||||
});
|
||||
|
||||
return client.db('test');
|
||||
};
|
||||
|
||||
(async function() {
|
||||
db = await connect();
|
||||
global.mongodb = db;
|
||||
|
||||
return;
|
||||
setInterval(async () => {
|
||||
const t = [];
|
||||
for (let i = 0; i < 32; i++) {
|
||||
t.push({
|
||||
playerID: 1,
|
||||
timestamp: new Date(),
|
||||
opcode: Math.floor(Math.random() * 120)
|
||||
});
|
||||
};
|
||||
|
||||
const bulkOps = t.map(doc => ({
|
||||
insertOne: doc
|
||||
}));
|
||||
|
||||
const n = performance.now();
|
||||
await db.collection("Actions").insertMany(t, {
|
||||
ordered: false,
|
||||
writeConcern: { w: 0 },
|
||||
bypassDocumentValidation: true
|
||||
});
|
||||
|
||||
/* await db.collection('Actions').bulkWrite(bulkOps, {
|
||||
ordered: false,
|
||||
writeConcern: { w: 0 },
|
||||
bypassDocumentValidation: true
|
||||
});*/
|
||||
}, 500);
|
||||
})();
|
||||
|
||||
module.exports = async function (ws, message, isBinary) {
|
||||
const player = players[ws.accountID];
|
||||
if (!player) return;
|
||||
|
||||
const isLimitReached = ws.limiter.update();
|
||||
if (isLimitReached) return ws.close();
|
||||
|
||||
if (player.isInactive) return; // this shouldn't happen, though
|
||||
|
||||
const view = new DataView(message);
|
||||
const packetID = view.getUint8(0);
|
||||
const packet = packetTable[packetID];
|
||||
|
||||
if (packetID !== 1 && !player.isAvailable) return;
|
||||
if (!packet) return;
|
||||
if (packet.expectedSize && packet.expectedSize !== view.byteLength) return console.info("expectedSize", view.byteLength, packet.expectedSize);
|
||||
if (!packet.isAvailable) return;
|
||||
|
||||
player.lastInteractionTimestamp = performance.now();
|
||||
|
||||
const metadata = packet.handler(ws, view, player) ?? [];
|
||||
|
||||
if (packetID > 1) {
|
||||
const action = {
|
||||
ts: new Date(),
|
||||
pid: player.id,
|
||||
opcode: packetID,
|
||||
metadata
|
||||
};
|
||||
|
||||
const relevantContext = global.actionContext.getElement(player.id).save(2000);
|
||||
relevantContext.push(action);
|
||||
console.info("asd", relevantContext.length)
|
||||
};
|
||||
};
|
||||
+239
@@ -0,0 +1,239 @@
|
||||
const Limiter = require("../Game/PacketLimiter");
|
||||
const EquipmentLimiter = require("../Game/EquipmentLimiter");
|
||||
const Inventory = require("../Game/Inventory");
|
||||
const Player = require("../Game/Player");
|
||||
const Game = require("../Game/Game");
|
||||
|
||||
const playerHandlers = require("../Handlers/Player");
|
||||
|
||||
const EnvironmentSetupPacket = require("../Packets/EnvironmentSetup");
|
||||
|
||||
const rewardCallback = require("../Callbacks/reward");
|
||||
const attackCallback = require("../Callbacks/attack");
|
||||
|
||||
const baseStarterRewards = require("../Data/startRewards");
|
||||
|
||||
const { maps, players, guilds } = Game;
|
||||
|
||||
const queries = require("../Data/queries");
|
||||
const { QUERY_FETCH_PLAYER_DATA, QUERY_FETCH_PLAYER_INFORMATION, QUERY_FETCH_GUILD_INFORMATION, QUERY_INSERT_DEFAULT_PLAYER, QUERY_FETCH_ACTIVE_QUESTS, QUERY_FETCH_STATUS, QUERY_FETCH_RESOURCES, QUERY_FETCH_EQUIPMENTS } = queries;
|
||||
|
||||
const playerEvents = Object.keys(playerHandlers);
|
||||
|
||||
const PlayerBehaviour = require("../Game/PlayerBehaviour");
|
||||
const DoubleLinkedList = require("../Utility/DoubleLinkedList");
|
||||
|
||||
global.actionContext = new DoubleLinkedList();
|
||||
|
||||
module.exports = async function (ws) {
|
||||
console.info("Before open for", ws.username, Game.state, ws.isAdmin);
|
||||
|
||||
if (Game.state === 0 && !ws.isAdmin) return ws.end(1337);
|
||||
|
||||
ws.connectedAt = Date.now();
|
||||
let abruptDisconnectTest = performance.now();
|
||||
|
||||
const ID = ws.accountID;
|
||||
const sessionID = Date.now().toString(36) + Math.random().toString(36).substring(2) + "!" + ID;
|
||||
|
||||
socket.sockets[sessionID] = ws;
|
||||
|
||||
ws.id = sessionID;
|
||||
ws.limiter = new Limiter({
|
||||
countThreshold: 10,
|
||||
timeThreshold: 1000
|
||||
});
|
||||
|
||||
const alreadyConnectedPlayer = players[ID];
|
||||
if (alreadyConnectedPlayer) {
|
||||
const player = alreadyConnectedPlayer;
|
||||
|
||||
const alreadyConnectedSoket = socket.sockets[player.sessionID];
|
||||
if (alreadyConnectedSoket) alreadyConnectedSoket.end(1);
|
||||
|
||||
player.sessionID = sessionID;
|
||||
ws.subscribe(player.map.id.toString());
|
||||
|
||||
player.map.removePlayer(player.id);
|
||||
player.map.addPlayer(player);
|
||||
|
||||
player.isUnderDisconnect = false;
|
||||
|
||||
player.cancelDisconnect();
|
||||
|
||||
const packet = EnvironmentSetupPacket(player.map.id);
|
||||
return ws.send(packet, true, true);
|
||||
};
|
||||
|
||||
const playerBehaviour = new PlayerBehaviour({
|
||||
size: 16,
|
||||
buffer: global.playerBehaviourContext,
|
||||
id: ID
|
||||
});
|
||||
|
||||
global.actionContext.addItem(ID, playerBehaviour);
|
||||
|
||||
const generalQuery = execute(QUERY_FETCH_PLAYER_DATA, [ID]);
|
||||
const informationQuery = execute(QUERY_FETCH_PLAYER_INFORMATION, [ID]);
|
||||
const guildQuery = execute(QUERY_FETCH_GUILD_INFORMATION, [ID]);
|
||||
const questQuery = execute(QUERY_FETCH_ACTIVE_QUESTS, [ID]);
|
||||
const statusQuery = execute(QUERY_FETCH_STATUS, [ID, true]);
|
||||
const resourcesQuery = execute(QUERY_FETCH_RESOURCES, [ID]);
|
||||
const equipmentsQuery = execute(QUERY_FETCH_EQUIPMENTS, [ID]);
|
||||
|
||||
const res = await Promise.all([generalQuery, informationQuery, guildQuery, questQuery, statusQuery, resourcesQuery, equipmentsQuery]);
|
||||
const hasError = res.some(q => q.error !== undefined);
|
||||
|
||||
if (hasError) return;
|
||||
if (ws.isInterrupted) return;
|
||||
|
||||
let isAlive = false;
|
||||
let map = null;
|
||||
let hitpoints = {
|
||||
current: 0,
|
||||
maximum: 0
|
||||
};
|
||||
let position = {};
|
||||
let attemptToDoubleLog = false;
|
||||
let maximumCannonRange = 0;
|
||||
let maximumHarpoonerRange = 0;
|
||||
let textureID = 0;
|
||||
let sight = 0;
|
||||
let speed = 0;
|
||||
let globalRank = 0;
|
||||
let levelRank = 0;
|
||||
|
||||
const playerFromDB = res[0].data[0];
|
||||
const guildFromDB = res[2].data[0];
|
||||
const questFromDB = res[3].data;
|
||||
const statusFromDB = res[4].data;
|
||||
const resourcesFromDB = res[5].data;
|
||||
const equipmentsFromDB = res[6].data;
|
||||
|
||||
const statusPoints = [];
|
||||
|
||||
const guildID = guildFromDB ? guildFromDB.id : 0;
|
||||
const guild = guilds[guildID];
|
||||
|
||||
const selectedAmmunition = playerFromDB ? playerFromDB.selectedAmmunitionID : 0;
|
||||
const selectedHarpoon = playerFromDB ? playerFromDB.selectedHarpoonID : 0;
|
||||
|
||||
const resourceSharedLimiter = {
|
||||
cannon: new EquipmentLimiter({
|
||||
maximum: 50
|
||||
}),
|
||||
harpooner: new EquipmentLimiter({
|
||||
maximum: 25
|
||||
}),
|
||||
sail: new EquipmentLimiter({
|
||||
maximum: 5
|
||||
}),
|
||||
pirate: new EquipmentLimiter({
|
||||
maximum: 250
|
||||
})
|
||||
};
|
||||
|
||||
const inventory = new Inventory({
|
||||
resourceSharedLimiter
|
||||
});
|
||||
|
||||
resourcesFromDB.forEach(r => {
|
||||
switch (r.categoryID) {
|
||||
case 6:
|
||||
statusPoints.push(r);
|
||||
break;
|
||||
|
||||
case 7:
|
||||
case 8:
|
||||
const equipment = equipmentsFromDB.find(e => e.id === r.id && e.categoryID === r.categoryID);
|
||||
r.amountEquipped = equipment ? equipment.amountEquipped : 0;
|
||||
break;
|
||||
};
|
||||
|
||||
inventory.refreshSingle(r);
|
||||
});
|
||||
|
||||
inventory.setSelectedAmmunition(selectedAmmunition);
|
||||
inventory.setSelectedHarpoon(selectedHarpoon);
|
||||
|
||||
if (!attemptToDoubleLog) {
|
||||
const playerHasData = playerFromDB ? true : false;
|
||||
if (!playerHasData) {
|
||||
const startingMap = maps.find(m => m.id === 1);
|
||||
const { x, y } = startingMap.grid.randomNode(10);
|
||||
|
||||
textureID = 1;
|
||||
sight = 450;
|
||||
speed = 250;
|
||||
globalRank = 1;
|
||||
levelRank = 1;
|
||||
isAlive = true;
|
||||
map = startingMap;
|
||||
position = {
|
||||
x,
|
||||
y
|
||||
};
|
||||
hitpoints = {
|
||||
current: 10000,
|
||||
maximum: 50000
|
||||
};
|
||||
|
||||
baseStarterRewards.forEach(r => inventory.refreshSingle(r));
|
||||
|
||||
await execute(QUERY_INSERT_DEFAULT_PLAYER, [ID, x, y, 1]);
|
||||
} else {
|
||||
textureID = playerFromDB.designID;
|
||||
sight = playerFromDB.sight;
|
||||
speed = playerFromDB.speed;
|
||||
globalRank = playerFromDB.globalRank;
|
||||
levelRank = playerFromDB.levelRank;
|
||||
isAlive = !!hitpoints;
|
||||
map = maps.find(m => m.id === playerFromDB.mapID);
|
||||
|
||||
position = {
|
||||
x: playerFromDB.x,
|
||||
y: playerFromDB.y
|
||||
};
|
||||
|
||||
hitpoints = {
|
||||
current: playerFromDB.currentHitpoints,
|
||||
maximum: playerFromDB.maximumHitpoints
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
const player = new Player({
|
||||
id: ID,
|
||||
sessionID: ws.id,
|
||||
name: ws.username,
|
||||
textureID,
|
||||
map,
|
||||
guild,
|
||||
positionX: position.x,
|
||||
positionY: position.y,
|
||||
isAlive,
|
||||
hitpoints,
|
||||
maximumCannonRange,
|
||||
maximumHarpoonerRange,
|
||||
sight,
|
||||
speed,
|
||||
globalRank,
|
||||
levelRank,
|
||||
inventory,
|
||||
statusPoints,
|
||||
activeStatusEffects: statusFromDB,
|
||||
quests: questFromDB,
|
||||
rewardCallback,
|
||||
attackCallback
|
||||
});
|
||||
|
||||
player.setDesign(textureID);
|
||||
|
||||
ws.subscribe(player.map.id.toString());
|
||||
|
||||
playerEvents.forEach(e => player.on(e, playerHandlers[e]));
|
||||
players[ID] = player;
|
||||
|
||||
const packet = EnvironmentSetupPacket(player.map.id);
|
||||
ws.send(packet, true, true);
|
||||
};
|
||||
@@ -0,0 +1,64 @@
|
||||
const crypto = require("crypto");
|
||||
|
||||
const redis = require("../Miscellaneous/redis");
|
||||
|
||||
const Game = require("../Game/Game");
|
||||
|
||||
const HTTP_BAD_REQUEST = "403";
|
||||
|
||||
function badRequest(res) {
|
||||
res.cork(() => res.isAborted ? null : res
|
||||
.writeStatus(HTTP_BAD_REQUEST)
|
||||
.end());
|
||||
};
|
||||
|
||||
module.exports = async function (res, req, context) {
|
||||
const cookieHeader = req.getHeader("cookie");
|
||||
const cookie = cookieHeader.split("ps=")[1];
|
||||
|
||||
res.onAborted(() => {
|
||||
res.isAborted = true;
|
||||
console.info("ABORTED!!!");
|
||||
});
|
||||
|
||||
if (!cookie) {
|
||||
console.info("upgrade 1")
|
||||
return badRequest(res);
|
||||
};
|
||||
|
||||
const secWebSocketKey = req.getHeader("sec-websocket-key");
|
||||
const secWebSocketProtocol = req.getHeader("sec-websocket-protocol");
|
||||
const secWebSocketExtensions = req.getHeader("sec-websocket-extensions");
|
||||
|
||||
const userAgent = req.getHeader("user-agent");
|
||||
const ip = req.getHeader("x-forwarded-for");
|
||||
|
||||
const formattedCookie = cookie.split(".")[0].slice(4);
|
||||
const cookieInDB = await redis.get(`sess:${formattedCookie}`);
|
||||
if (!cookieInDB) {
|
||||
console.info("upgrade 2")
|
||||
|
||||
return badRequest(res);
|
||||
};
|
||||
|
||||
const parsedCookie = JSON.parse(cookieInDB);
|
||||
if (!parsedCookie.accountID) {
|
||||
console.info("upgrade 3")
|
||||
|
||||
return badRequest(res);
|
||||
};
|
||||
|
||||
const hash = crypto.createHash("md5")
|
||||
.update(userAgent)
|
||||
.update(ip)
|
||||
.digest("hex");
|
||||
|
||||
// if (hash !== parsedCookie.fingerprint) return badRequest(res);
|
||||
const { accountID, username, isAdmin } = parsedCookie;
|
||||
console.info("Before upgrading connection for", username);
|
||||
res.cork(() => res.upgrade({ accountID, username, cookie, isAdmin },
|
||||
secWebSocketKey,
|
||||
secWebSocketProtocol,
|
||||
secWebSocketExtensions,
|
||||
context));
|
||||
};
|
||||
Reference in New Issue
Block a user