Skip to content

Commit

Permalink
v0.5.0
Browse files Browse the repository at this point in the history
  • Loading branch information
dapucita authored Mar 16, 2021
2 parents 96912a2 + 04cf953 commit 4ceb50f
Show file tree
Hide file tree
Showing 54 changed files with 2,616 additions and 1,140 deletions.
7 changes: 6 additions & 1 deletion README.ko.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ Language : [English](README.md), Korean(한국어)
[Patreon](https://www.patreon.com/dapucita)을 통해 이 프로젝트에 기부하고 지원해주세요!

## 특징
![Node.js](https://img.shields.io/badge/-Node.js-339933?style=for-the-badge&logo=node%2ejs&logoColor=fff)
![Typescript](https://img.shields.io/badge/-Typescript-007acc?style=for-the-badge&logo=typescript&logoColor=fff)
![React](https://img.shields.io/badge/-React-61dafb?style=for-the-badge&logo=react&logoColor=fff)
![SQLite](https://img.shields.io/badge/-SQLite-003b57?style=for-the-badge&logo=sqlite&logoColor=fff)
![Chrome](https://img.shields.io/badge/-Chrome-4285f4?style=for-the-badge&logo=google%20chrome&logoColor=fff)
- Windows, Linux, OS X 등 멀티 플랫폼 지원
- 웹 기반 관리 시스템
- 게임 자동 운영
Expand All @@ -30,7 +35,7 @@ Language : [English](README.md), Korean(한국어)
- [Game Playing](https://github.com/dapucita/haxbotron/wiki/Game-Playing)
- [Core Server](https://github.com/dapucita/haxbotron/wiki/%5BKorean%5D-Core-Server)
- [DB Server](https://github.com/dapucita/haxbotron/wiki/DB-Server)
- ... [위키](https://github.com/dapucita/haxbotron/wiki)`docs` 폴더에 있는 문서도 참고하세요
- ... [위키](https://github.com/dapucita/haxbotron/wiki) 참고하세요

## 사용하기
[처음 시작하기](https://github.com/dapucita/haxbotron/wiki/%5BKorean%5D-%EC%B2%98%EC%9D%8C-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0) 문서에 따라 과정을 진행하면 쉽게 `Haxbotron`을 시작할 수 있습니다.
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ Please donate and support this project by [Patreon](https://www.patreon.com/dapu
- [Game Playing](https://github.com/dapucita/haxbotron/wiki/Game-Playing)
- [Core Server](https://github.com/dapucita/haxbotron/wiki/Core-Server)
- [DB Server](https://github.com/dapucita/haxbotron/wiki/DB-Server)
- ...and check also our [wiki](https://github.com/dapucita/haxbotron/wiki) and documents in `docs`.
- ...and check also our [wiki](https://github.com/dapucita/haxbotron/wiki).

## How to Use
You can easily start by following the document [Getting Started](https://github.com/dapucita/haxbotron/wiki/Getting-Started).
Expand Down
10 changes: 0 additions & 10 deletions core/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,6 @@ const coreServerSettings = {
const buildOutputDirectory = path.resolve(__dirname, "../public");
const whiteListIPs: string[] = process.env.SERVER_WHITELIST_IP?.split(",") || ['127.0.0.1'];

/*
if (process.env.TWEAKS_GEOLOCATIONOVERRIDE && JSON.parse(process.env.TWEAKS_GEOLOCATIONOVERRIDE.toLowerCase()) === true) {
hostRoomConfig.geo = {
code: process.env.TWEAKS_GEOLOCATIONOVERRIDE_CODE || "KR"
, lat: parseFloat(process.env.TWEAKS_GEOLOCATIONOVERRIDE_LAT || "37.5665")
, lon: parseFloat(process.env.TWEAKS_GEOLOCATIONOVERRIDE_LON || "126.978")
}
}
*/

nodeStorage.init();

browser.attachSIOserver(sio);
Expand Down
13 changes: 11 additions & 2 deletions core/game/bot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,18 @@ window.gameRoom = {
_room: window.HBInit(loadedConfig._config)
,config: loadedConfig
,link: ''
,stadiumData:{
,stadiumData: {
default: localStorage.getItem('_defaultMap')!
,training: localStorage.getItem('_readyMap')!
}
,bannedWordsPool: {
nickname: []
,chat: []
}
,teamColours: {
red: { angle: 0, textColour: 0xffffff, teamColour1: 0xe66e55, teamColour2: 0xe66e55, teamColour3: 0xe66e55 }
,blue: { angle: 0, textColour: 0xffffff, teamColour1: 0x5a89e5, teamColour2: 0x5a89e5, teamColour3: 0x5a89e5 }
}
,logger: Logger.getInstance()
,isStatRecord: false
,isGamingNow: false
Expand Down Expand Up @@ -87,9 +95,10 @@ var scheduledTimer = setInterval(() => {
placeholderScheduler.targetName = player.name;

// check muted player and unmute when it's time to unmute
if (player.permissions.mute === true && nowTimeStamp > player.permissions.muteExpire) {
if (player.permissions.mute === true && player.permissions.muteExpire !== -1 && nowTimeStamp > player.permissions.muteExpire) {
player.permissions.mute = false; //unmute
window.gameRoom._room.sendAnnouncement(Tst.maketext(LangRes.scheduler.autoUnmute, placeholderScheduler), null, 0x479947, "normal", 0); //notify it
window._emitSIOPlayerStatusChangeEvent(player.id);
}

// when afk too long kick option is enabled, then check sleeping with afk command and kick if afk too long
Expand Down
32 changes: 32 additions & 0 deletions core/game/controller/TextFilter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { AhoCorasick } from '../model/TextFilter/filter'

/**
* Check if given string is already used in the game room (check duplicated nickname)
* @param compare plain text to be compared
* @returns return `true` when already in use
*/
export function isExistNickname(compare: string): boolean {
for (let eachPlayer of window.gameRoom.playerList.values()) {
if(eachPlayer.name.trim() === compare.trim()) {
return true;
}
}
return false;
}

/**
* Check if given string includes banned words from nickname filter
* @param pool banned words list
* @param compare plain text to be compared
* @returns return `true` when includes banned word(s)
*/
export function isIncludeBannedWords(pool: string[], compare: string): boolean {
const ac = new AhoCorasick(pool);
const results = ac.search(compare);

if(Array.isArray(results) && results.length) {
return true;
} else {
return false;
}
}
2 changes: 2 additions & 0 deletions core/game/controller/commands/afk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,4 +64,6 @@ export function cmdAfk(byPlayer: PlayerObject, message?: string): void {
window.gameRoom.isStatRecord = false;
}
}

window._emitSIOPlayerStatusChangeEvent(byPlayer.id);
}
2 changes: 2 additions & 0 deletions core/game/controller/commands/freeze.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ export function cmdFreeze(byPlayer: PlayerObject): void {
window.gameRoom.isMuteAll = true; //on
window.gameRoom._room.sendAnnouncement(LangRes.command.freeze.onFreeze, null, 0x479947, "normal", 1);
}

window._emitSIOPlayerStatusChangeEvent(byPlayer.id);
} else {
window.gameRoom._room.sendAnnouncement(LangRes.command.freeze._ErrorNoPermission, byPlayer.id, 0xFF7777, "normal", 2);
}
Expand Down
2 changes: 2 additions & 0 deletions core/game/controller/commands/mute.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ export function cmdMute(byPlayer: PlayerObject, message?: string): void {
window.gameRoom._room.sendAnnouncement(Tst.maketext(LangRes.command.mute.successMute, placeholder), null, 0x479947, "normal", 1);
}
}

window._emitSIOPlayerStatusChangeEvent(byPlayer.id);
} else {
window.gameRoom._room.sendAnnouncement(LangRes.command.mute._ErrorNoPlayer, byPlayer.id, 0xFF7777, "normal", 2);
}
Expand Down
4 changes: 4 additions & 0 deletions core/game/controller/commands/super.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ export async function cmdSuper(byPlayer: PlayerObject, message?: string, submess
//setPlayerData(playerList.get(playerID)); // update
window.gameRoom._room.sendAnnouncement(LangRes.command.super.loginSuccess, byPlayer.id, 0x479947, "normal", 2);
window.gameRoom.logger.i('super', `${byPlayer.name}#${byPlayer.id} did successfully login to super admin with the key. (KEY ${submessage})`);

window._emitSIOPlayerStatusChangeEvent(byPlayer.id);
} else {
window.gameRoom.playerList.get(byPlayer.id)!.permissions.malActCount++; // add malicious behaviour count
window.gameRoom._room.sendAnnouncement(LangRes.command.super.loginFail, byPlayer.id, 0xFF7777, "normal", 2);
Expand All @@ -39,6 +41,8 @@ export async function cmdSuper(byPlayer: PlayerObject, message?: string, submess
//setPlayerData(playerList.get(playerID)); // update
window.gameRoom._room.sendAnnouncement(LangRes.command.super.logoutSuccess, byPlayer.id, 0x479947, "normal", 2);
window.gameRoom.logger.i('super', `${byPlayer.name}#${byPlayer.id} did logout from super admin.`);

window._emitSIOPlayerStatusChangeEvent(byPlayer.id);
} else {
window.gameRoom._room.sendAnnouncement(LangRes.command.super._ErrorNoPermission, byPlayer.id, 0xFF7777, "normal", 2);
}
Expand Down
2 changes: 2 additions & 0 deletions core/game/controller/events/onPlayerAdminChange.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,6 @@ export function onPlayerAdminChangeListener(changedPlayer: PlayerObject, byPlaye
if(window.gameRoom.config.rules.autoAdmin === true) { // if auto admin option is enabled
updateAdmins(); // check when the last admin player disqulified by self
}

window._emitSIOPlayerStatusChangeEvent(changedPlayer.id);
}
13 changes: 13 additions & 0 deletions core/game/controller/events/onPlayerChat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { PlayerObject } from "../../model/GameObject/PlayerObject";
import { isCommandString, parseCommand } from "../Parser";
import { getUnixTimestamp } from "../Statistics";
import { convertTeamID2Name, TeamID } from "../../model/GameObject/TeamID";
import { isIncludeBannedWords } from "../TextFilter";

export function onPlayerChatListener(player: PlayerObject, message: string): boolean {
// Event called when a player sends a chat message.
Expand Down Expand Up @@ -60,6 +61,8 @@ export function onPlayerChatListener(player: PlayerObject, message: string): boo
window.gameRoom.playerList.get(player.id)!.permissions['mute'] = true; // mute this player
window.gameRoom.playerList.get(player.id)!.permissions.muteExpire = nowTimeStamp + window.gameRoom.config.settings.muteDefaultMillisecs; //record mute expiration date by unix timestamp
window.gameRoom._room.sendAnnouncement(Tst.maketext(LangRes.antitrolling.chatFlood.muteReason, placeholderChat), null, 0xFF0000, "normal", 1); // notify that fact

window._emitSIOPlayerStatusChangeEvent(player.id);
return false;
}
}
Expand All @@ -68,6 +71,16 @@ export function onPlayerChatListener(player: PlayerObject, message: string): boo
window.gameRoom._room.sendAnnouncement(Tst.maketext(LangRes.onChat.tooLongChat, placeholderChat), player.id, 0xFF0000, "bold", 2); // notify that fact
return false;
}
// if this player use seperator (|,|) in chat message
if(message.includes('|,|')) {
window.gameRoom._room.sendAnnouncement(Tst.maketext(LangRes.onChat.includeSeperator, placeholderChat), player.id, 0xFF0000, "bold", 2); // notify that fact
return false;
}
// Check if includes banned words
if(window.gameRoom.config.settings.chatTextFilter === true && isIncludeBannedWords(window.gameRoom.bannedWordsPool.chat, message)) {
window.gameRoom._room.sendAnnouncement(Tst.maketext(LangRes.onChat.bannedWords, placeholderChat), player.id, 0xFF0000, "bold", 2); // notify that fact
return false;
}
// otherwise, send to room
return true;
}
Expand Down
24 changes: 23 additions & 1 deletion core/game/controller/events/onPlayerJoin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { setDefaultStadiums, updateAdmins } from "../RoomTools";
import { convertTeamID2Name, TeamID } from "../../model/GameObject/TeamID";
import { putTeamNewPlayerConditional, roomActivePlayersNumberCheck } from "../../model/OperateHelper/Quorum";
import { decideTier, getAvatarByTier, Tier } from "../../model/Statistics/Tier";
import { isExistNickname, isIncludeBannedWords } from "../TextFilter";

export async function onPlayerJoinListener(player: PlayerObject): Promise<void> {
const joinTimeStamp: number = getUnixTimestamp();
Expand Down Expand Up @@ -61,6 +62,13 @@ export async function onPlayerJoinListener(player: PlayerObject): Promise<void>
// window.room.clearBan(player.id); //useless cuz banned player in haxball couldn't make join-event.
}
}

// if this player use seperator (|,|) in nickname, then kick
if (player.name.includes('|,|')) {
window.gameRoom.logger.i('onPlayerJoin', `${player.name}#${player.id} was joined but kicked for including seperator word. (|,|)`);
window.gameRoom._room.kickPlayer(player.id, Tst.maketext(LangRes.onJoin.includeSeperator, placeholderJoin), false); // kick
return;
}

// if this player has already joinned by other connection
for (let eachPlayer of window.gameRoom.playerList.values()) {
Expand All @@ -72,13 +80,27 @@ export async function onPlayerJoinListener(player: PlayerObject): Promise<void>
}
}

// if player's nickname is logger than limitation
// if player's nickname is longer than limitation
if (player.name.length > window.gameRoom.config.settings.nicknameLengthLimit) {
window.gameRoom.logger.i('onPlayerJoin', `${player.name}#${player.id} was joined but kicked for too long nickname.`);
window.gameRoom._room.kickPlayer(player.id, Tst.maketext(LangRes.onJoin.tooLongNickname, placeholderJoin), false); // kick
return;
}

// if player's nickname is already used (duplicated nickname)
if (window.gameRoom.config.settings.forbidDuplicatedNickname === true && isExistNickname(player.name) === true) {
window.gameRoom.logger.i('onPlayerJoin', `${player.name}#${player.id} was joined but kicked for duplicated nickname.`);
window.gameRoom._room.kickPlayer(player.id, Tst.maketext(LangRes.onJoin.duplicatedNickname, placeholderJoin), false); // kick
return;
}

// if player's nickname includes some banned words
if (window.gameRoom.config.settings.nicknameTextFilter === true && isIncludeBannedWords(window.gameRoom.bannedWordsPool.nickname, player.name) === true) {
window.gameRoom.logger.i('onPlayerJoin', `${player.name}#${player.id} was joined but kicked for including banned word(s).`);
window.gameRoom._room.kickPlayer(player.id, Tst.maketext(LangRes.onJoin.bannedNickname, placeholderJoin), false); // kick
return;
}

// add the player who joined into playerList by creating class instance
let existPlayerData = await getPlayerDataFromDB(player.auth);
if (existPlayerData !== undefined) {
Expand Down
2 changes: 2 additions & 0 deletions core/game/controller/events/onPlayerTeamChange.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,6 @@ export function onPlayerTeamChangeListener(changedPlayer: PlayerObject, byPlayer
window.gameRoom.playerList.get(changedPlayer.id)!.team = changedPlayer.team;
window.gameRoom.logger.i('onPlayerTeamChange', `${changedPlayer.name}#${changedPlayer.id} is moved team to ${convertTeamID2Name(changedPlayer.team)}.`);
}

window._emitSIOPlayerStatusChangeEvent(changedPlayer.id);
}
4 changes: 4 additions & 0 deletions core/game/model/Configuration/GameRoomSettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,8 @@ export interface GameRoomSettings {

nicknameLengthLimit : number
chatLengthLimit : number

forbidDuplicatedNickname: boolean
nicknameTextFilter: boolean
chatTextFilter: boolean
}
107 changes: 107 additions & 0 deletions core/game/model/TextFilter/filter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/*
Text filter for Haxbotron by dapucita
============================
Implementation of the Aho-Corasick string searching algorithm
Source code originally from https://github.com/BrunoRB/ahocorasick
var AhoCorasick = require('ahocorasick');
var ac = new AhoCorasick(['keyword1', 'keyword2', 'etc']);
var results = ac.search('should find keyword1 at position 19 and keyword2 at position 47.');
// [ [ 19, [ 'keyword1' ] ], [ 47, [ 'keyword2' ] ] ]
============================
MIT License
*/

export class AhoCorasick {
constructor(keywords) {
this._buildTables(keywords);
}
_buildTables(keywords) {
var gotoFn = {
0: {}
};
var output = {};

var state = 0;
keywords.forEach(function (word) {
var curr = 0;
for (var i = 0; i < word.length; i++) {
var l = word[i];
if (gotoFn[curr] && l in gotoFn[curr]) {
curr = gotoFn[curr][l];
}
else {
state++;
gotoFn[curr][l] = state;
gotoFn[state] = {};
curr = state;
output[state] = [];
}
}

output[curr].push(word);
});

var failure = {};
var xs = [];

// f(s) = 0 for all states of depth 1 (the ones from which the 0 state can transition to)
for (var l in gotoFn[0]) {
var state = gotoFn[0][l];
failure[state] = 0;
xs.push(state);
}

while (xs.length) {
var r = xs.shift();
// for each symbol a such that g(r, a) = s
for (var l in gotoFn[r]) {
var s = gotoFn[r][l];
xs.push(s);

// set state = f(r)
var state = failure[r];
while (state > 0 && !(l in gotoFn[state])) {
state = failure[state];
}

if (l in gotoFn[state]) {
var fs = gotoFn[state][l];
failure[s] = fs;
output[s] = output[s].concat(output[fs]);
}
else {
failure[s] = 0;
}
}
}

this.gotoFn = gotoFn;
this.output = output;
this.failure = failure;
}
search(string) {
var state = 0;
var results = [];
for (var i = 0; i < string.length; i++) {
var l = string[i];
while (state > 0 && !(l in this.gotoFn[state])) {
state = this.failure[state];
}
if (!(l in this.gotoFn[state])) {
continue;
}

state = this.gotoFn[state][l];

if (this.output[state].length) {
var foundStrs = this.output[state];
results.push([i, foundStrs]);
}
}

return results;
}
}
Loading

0 comments on commit 4ceb50f

Please sign in to comment.