Full code added

This commit is contained in:
Z-Tavern 2024-10-18 00:19:19 +00:00
parent d48fa5c54a
commit cb9bea73ec
284 changed files with 94999 additions and 1 deletions

View file

@ -1 +1,51 @@
# ZTavern
# ZTavern Bo2 Mods & Server setup
## Install
- Setup a Bo2 Server
https://plutonium.pw/docs/server/t6/setting-up-a-server/
- Install Fed's Node Server Manager
https://github.com/alicealys/node-server-manager
- Copy & paste all folder contents
node-server-manager -> node-server-manager installed folder
bo2 -> your bo2 server root directory
t6 -> %localappdata%\Plutonium\storage\t6
### Server keys
- Make sure to add your Plutonium server keys in all "!start_zm_serverxxx.bat"
- Change the gamelogs path for each server according to yours in node-server-manager/Configuration/NSMConfiguration.json
**Server name**
- Server key name must be named as follow :
(Your server name) | BRUTUS ON THE BRIDGE | (extra txt)
- Server name list :
PRIVATE SERVER
RAID BOSS
BRUTUS ON THE BRIDGE
TRANZIT IN THE BUS
PANZER IN AGARTHA
ORIGINS
ORIGINS2
BURIED
TRANZIT2
DIE RISE
TOWN
TOWN2
TOWN3
MOTD
NUKETOWN
### Auto server restart .bat file
- Depending on where you installed node-server-manager folder, edit the path used in bo2/!restart_servers.bat
## How to run
- bo2/!restart_servers.bat start and restart all servers along with NSM every 6 hours
## Misc.
### Where to look in NSM folders ?
- 99% of my work on the NodeJS side is located in the Plugin folder : ZombieBank.js, ClanTag.js & ZombieStats.js
### Add moderator permissions
- Add the .id of target in staff.gsc (t6/scripts directory)
- Add the .pguid of target in ZombieBank.js, ClanTag.js, ZombieStats.js, NativeCommands.js

41
bo2/!restart_servers.bat Normal file
View file

@ -0,0 +1,41 @@
@echo off
call getCmdPID
set "current_pid=%errorlevel%"
:loop
for /f "skip=3 tokens=2 delims= " %%a in ('tasklist /fi "imagename eq cmd.exe"') do (
if "%%a" neq "%current_pid%" (
TASKKILL /PID %%a /f >nul 2>nul
)
)
taskkill /f /im node.exe
taskkill /f /im plutonium-bootstrapper-win32.exe
start !start_zm_serverdierise.bat
start !start_zm_servernuketown.bat
start !start_zm_serverburied.bat
start !start_zm_serverprivate.bat
start !start_zm_servermotd.bat
timeout 5 >nul
start !start_zm_servertitb.bat
start !start_zm_serverpanzer.bat
start !start_zm_serverbrutus.bat
start !start_zm_serverorigin.bat
start !start_zm_serverorigin2.bat
timeout 5 >nul
start !start_zm_serverraid.bat
start !start_zm_servertown.bat
start !start_zm_servertown2.bat
start !start_zm_servertown3.bat
start !start_zm_servertranzit.bat
timeout 50 >nul
:: Replace path depending on where you put NSM folder
:: StartNSM.bat must be started while being on the same directory, hence why I use "cd" to move around
cd C:\bo2\node-server-manager
start "" cmd /c C:\bo2\node-server-manager\StartNSM.bat
cd C:\bo2
timeout 21540 >nul
goto loop

View file

@ -0,0 +1,23 @@
@echo off
::Paste the server key from https://platform.plutonium.pw/serverkeys here
set key=your_key
::Name of the config file the server should use. (default: dedicated_zm.cfg)
set cfg=dedicated_zmbrutus.cfg
::Name of the server shown in the title of the cmd window. This will NOT bet shown ingame.
set name=.
::Port used by the server (default: 4976)
set port=30001
::Only change this when you don't want to keep the bat files in the game folder. MOST WON'T NEED TO EDIT THIS!
set gamepath=%cd%
title BRUTUS
echo Visit plutonium.pw / Join the Discord (a6JM2Tv) for NEWS and Updates!
echo Server "%name%" will load %cfg% and listen on port %port% UDP!
echo To shut down the server close this window first!
echo (%date%) - (%time%) %name% server start.
cd /D %LOCALAPPDATA%\Plutonium
:server
start /wait /abovenormal bin\plutonium-bootstrapper-win32.exe t6zm "%gamepath%" -dedicated +set key %key% +sv_config %cfg% +net_port %port%
echo (%date%) - (%time%) WARNING: %name% server closed or dropped... server restarts.
goto server

View file

@ -0,0 +1,23 @@
@echo off
::Paste the server key from https://platform.plutonium.pw/serverkeys here
set key=your_key
::Name of the config file the server should use. (default: dedicated_zm.cfg)
set cfg=dedicated_zmburied.cfg
::Name of the server shown in the title of the cmd window. This will NOT bet shown ingame.
set name=Buried
::Port used by the server (default: 4976)
set port=30002
::Only change this when you don't want to keep the bat files in the game folder. MOST WON'T NEED TO EDIT THIS!
set gamepath=%cd%
title Buried
echo Visit plutonium.pw / Join the Discord (a6JM2Tv) for NEWS and Updates!
echo Server "%name%" will load %cfg% and listen on port %port% UDP!
echo To shut down the server close this window first!
echo (%date%) - (%time%) %name% server start.
cd /D %LOCALAPPDATA%\Plutonium
:server
start /wait /abovenormal bin\plutonium-bootstrapper-win32.exe t6zm "%gamepath%" -dedicated +set key %key% +sv_config %cfg% +net_port %port%
echo (%date%) - (%time%) WARNING: %name% server closed or dropped... server restarts.
goto server

View file

@ -0,0 +1,23 @@
@echo off
::Paste the server key from https://platform.plutonium.pw/serverkeys here
set key=your_key
::Name of the config file the server should use. (default: dedicated_zm.cfg)
set cfg=dedicated_zmdierise.cfg
::Name of the server shown in the title of the cmd window. This will NOT bet shown ingame.
set name=DieRise
::Port used by the server (default: 4976)
set port=30003
::Only change this when you don't want to keep the bat files in the game folder. MOST WON'T NEED TO EDIT THIS!
set gamepath=%cd%
title Die Rise
echo Visit plutonium.pw / Join the Discord (a6JM2Tv) for NEWS and Updates!
echo Server "%name%" will load %cfg% and listen on port %port% UDP!
echo To shut down the server close this window first!
echo (%date%) - (%time%) %name% server start.
cd /D %LOCALAPPDATA%\Plutonium
:server
start /wait /abovenormal bin\plutonium-bootstrapper-win32.exe t6zm "%gamepath%" -dedicated +set key %key% +sv_config %cfg% +net_port %port%
echo (%date%) - (%time%) WARNING: %name% server closed or dropped... server restarts.
goto server

View file

@ -0,0 +1,23 @@
@echo off
::Paste the server key from https://platform.plutonium.pw/serverkeys here
set key=your_key
::Name of the config file the server should use. (default: dedicated_zm.cfg)
set cfg=dedicated_zmmotd.cfg
::Name of the server shown in the title of the cmd window. This will NOT bet shown ingame.
set name=.
::Port used by the server (default: 4976)
set port=30004
::Only change this when you don't want to keep the bat files in the game folder. MOST WON'T NEED TO EDIT THIS!
set gamepath=%cd%
title MOTD
echo Visit plutonium.pw / Join the Discord (a6JM2Tv) for NEWS and Updates!
echo Server "%name%" will load %cfg% and listen on port %port% UDP!
echo To shut down the server close this window first!
echo (%date%) - (%time%) %name% server start.
cd /D %LOCALAPPDATA%\Plutonium
:server
start /wait /abovenormal bin\plutonium-bootstrapper-win32.exe t6zm "%gamepath%" -dedicated +set key %key% +sv_config %cfg% +net_port %port%
echo (%date%) - (%time%) WARNING: %name% server closed or dropped... server restarts.
goto server

View file

@ -0,0 +1,23 @@
@echo off
::Paste the server key from https://platform.plutonium.pw/serverkeys here
set key=your_key
::Name of the config file the server should use. (default: dedicated_zm.cfg)
set cfg=dedicated_zmnuketown.cfg
::Name of the server shown in the title of the cmd window. This will NOT bet shown ingame.
set name=.
::Port used by the server (default: 4976)
set port=30006
::Only change this when you don't want to keep the bat files in the game folder. MOST WON'T NEED TO EDIT THIS!
set gamepath=%cd%
title Nuketown
echo Visit plutonium.pw / Join the Discord (a6JM2Tv) for NEWS and Updates!
echo Server "%name%" will load %cfg% and listen on port %port% UDP!
echo To shut down the server close this window first!
echo (%date%) - (%time%) %name% server start.
cd /D %LOCALAPPDATA%\Plutonium
:server
start /wait /abovenormal bin\plutonium-bootstrapper-win32.exe t6zm "%gamepath%" -dedicated +set key %key% +sv_config %cfg% +net_port %port%
echo (%date%) - (%time%) WARNING: %name% server closed or dropped... server restarts.
goto server

View file

@ -0,0 +1,23 @@
@echo off
::Paste the server key from https://platform.plutonium.pw/serverkeys here
set key=your_key
::Name of the config file the server should use. (default: dedicated_zm.cfg)
set cfg=dedicated_zmorigin.cfg
::Name of the server shown in the title of the cmd window. This will NOT bet shown ingame.
set name=.
::Port used by the server (default: 4976)
set port=30007
::Only change this when you don't want to keep the bat files in the game folder. MOST WON'T NEED TO EDIT THIS!
set gamepath=%cd%
title Origin 1
echo Visit plutonium.pw / Join the Discord (a6JM2Tv) for NEWS and Updates!
echo Server "%name%" will load %cfg% and listen on port %port% UDP!
echo To shut down the server close this window first!
echo (%date%) - (%time%) %name% server start.
cd /D %LOCALAPPDATA%\Plutonium
:server
start /wait /abovenormal bin\plutonium-bootstrapper-win32.exe t6zm "%gamepath%" -dedicated +set key %key% +sv_config %cfg% +net_port %port%
echo (%date%) - (%time%) WARNING: %name% server closed or dropped... server restarts.
goto server

View file

@ -0,0 +1,23 @@
@echo off
::Paste the server key from https://platform.plutonium.pw/serverkeys here
set key=your_key
::Name of the config file the server should use. (default: dedicated_zm.cfg)
set cfg=dedicated_zmorigin2.cfg
::Name of the server shown in the title of the cmd window. This will NOT bet shown ingame.
set name=.
::Port used by the server (default: 4976)
set port=30008
::Only change this when you don't want to keep the bat files in the game folder. MOST WON'T NEED TO EDIT THIS!
set gamepath=%cd%
title Origin 2
echo Visit plutonium.pw / Join the Discord (a6JM2Tv) for NEWS and Updates!
echo Server "%name%" will load %cfg% and listen on port %port% UDP!
echo To shut down the server close this window first!
echo (%date%) - (%time%) %name% server start.
cd /D %LOCALAPPDATA%\Plutonium
:server
start /wait /abovenormal bin\plutonium-bootstrapper-win32.exe t6zm "%gamepath%" -dedicated +set key %key% +sv_config %cfg% +net_port %port%
echo (%date%) - (%time%) WARNING: %name% server closed or dropped... server restarts.
goto server

View file

@ -0,0 +1,23 @@
@echo off
::Paste the server key from https://platform.plutonium.pw/serverkeys here
set key=your_key
::Name of the config file the server should use. (default: dedicated_zm.cfg)
set cfg=dedicated_zmpanzer.cfg
::Name of the server shown in the title of the cmd window. This will NOT bet shown ingame.
set name=.
::Port used by the server (default: 4976)
set port=30010
::Only change this when you don't want to keep the bat files in the game folder. MOST WON'T NEED TO EDIT THIS!
set gamepath=%cd%
title PANZER
echo Visit plutonium.pw / Join the Discord (a6JM2Tv) for NEWS and Updates!
echo Server "%name%" will load %cfg% and listen on port %port% UDP!
echo To shut down the server close this window first!
echo (%date%) - (%time%) %name% server start.
cd /D %LOCALAPPDATA%\Plutonium
:server
start /wait /abovenormal bin\plutonium-bootstrapper-win32.exe t6zm "%gamepath%" -dedicated +set key %key% +sv_config %cfg% +net_port %port%
echo (%date%) - (%time%) WARNING: %name% server closed or dropped... server restarts.
goto server

View file

@ -0,0 +1,23 @@
@echo off
::Paste the server key from https://platform.plutonium.pw/serverkeys here
set key=your_key
::Name of the config file the server should use. (default: dedicated_zm.cfg)
set cfg=dedicated_zmprivate.cfg
::Name of the server shown in the title of the cmd window. This will NOT bet shown ingame.
set name=.
::Port used by the server (default: 4976)
set port=30005
::Only change this when you don't want to keep the bat files in the game folder. MOST WON'T NEED TO EDIT THIS!
set gamepath=%cd%
title PRIVATE
echo Visit plutonium.pw / Join the Discord (a6JM2Tv) for NEWS and Updates!
echo Server "%name%" will load %cfg% and listen on port %port% UDP!
echo To shut down the server close this window first!
echo (%date%) - (%time%) %name% server start.
cd /D %LOCALAPPDATA%\Plutonium
:server
start /wait /abovenormal bin\plutonium-bootstrapper-win32.exe t6zm "%gamepath%" -dedicated +set key %key% +sv_config %cfg% +net_port %port%
echo (%date%) - (%time%) WARNING: %name% server closed or dropped... server restarts.
goto server

View file

@ -0,0 +1,23 @@
@echo off
::Paste the server key from https://platform.plutonium.pw/serverkeys here
set key=your_key
::Name of the config file the server should use. (default: dedicated_zm.cfg)
set cfg=dedicated_zmorigin3.cfg
::Name of the server shown in the title of the cmd window. This will NOT bet shown ingame.
set name=.
::Port used by the server (default: 4976)
set port=30009
::Only change this when you don't want to keep the bat files in the game folder. MOST WON'T NEED TO EDIT THIS!
set gamepath=%cd%
title Origin 3
echo Visit plutonium.pw / Join the Discord (a6JM2Tv) for NEWS and Updates!
echo Server "%name%" will load %cfg% and listen on port %port% UDP!
echo To shut down the server close this window first!
echo (%date%) - (%time%) %name% server start.
cd /D %LOCALAPPDATA%\Plutonium
:server
start /wait /abovenormal bin\plutonium-bootstrapper-win32.exe t6zm "%gamepath%" -dedicated +set key %key% +sv_config %cfg% +net_port %port%
echo (%date%) - (%time%) WARNING: %name% server closed or dropped... server restarts.
goto server

View file

@ -0,0 +1,23 @@
@echo off
::Paste the server key from https://platform.plutonium.pw/serverkeys here
set key=your_key
::Name of the config file the server should use. (default: dedicated_zm.cfg)
set cfg=dedicated_zmtitb.cfg
::Name of the server shown in the title of the cmd window. This will NOT bet shown ingame.
set name=.
::Port used by the server (default: 4976)
set port=30011
::Only change this when you don't want to keep the bat files in the game folder. MOST WON'T NEED TO EDIT THIS!
set gamepath=%cd%
title TITB
echo Visit plutonium.pw / Join the Discord (a6JM2Tv) for NEWS and Updates!
echo Server "%name%" will load %cfg% and listen on port %port% UDP!
echo To shut down the server close this window first!
echo (%date%) - (%time%) %name% server start.
cd /D %LOCALAPPDATA%\Plutonium
:server
start /wait /abovenormal bin\plutonium-bootstrapper-win32.exe t6zm "%gamepath%" -dedicated +set key %key% +sv_config %cfg% +net_port %port%
echo (%date%) - (%time%) WARNING: %name% server closed or dropped... server restarts.
goto server

View file

@ -0,0 +1,23 @@
@echo off
::Paste the server key from https://platform.plutonium.pw/serverkeys here
set key=your_key
::Name of the config file the server should use. (default: dedicated_zm.cfg)
set cfg=dedicated_zmtown.cfg
::Name of the server shown in the title of the cmd window. This will NOT bet shown ingame.
set name=.
::Port used by the server (default: 4976)
set port=30012
::Only change this when you don't want to keep the bat files in the game folder. MOST WON'T NEED TO EDIT THIS!
set gamepath=%cd%
title Town
echo Visit plutonium.pw / Join the Discord (a6JM2Tv) for NEWS and Updates!
echo Server "%name%" will load %cfg% and listen on port %port% UDP!
echo To shut down the server close this window first!
echo (%date%) - (%time%) %name% server start.
cd /D %LOCALAPPDATA%\Plutonium
:server
start /wait /abovenormal bin\plutonium-bootstrapper-win32.exe t6zm "%gamepath%" -dedicated +set key %key% +sv_config %cfg% +net_port %port%
echo (%date%) - (%time%) WARNING: %name% server closed or dropped... server restarts.
goto server

View file

@ -0,0 +1,23 @@
@echo off
::Paste the server key from https://platform.plutonium.pw/serverkeys here
set key=your_key
::Name of the config file the server should use. (default: dedicated_zm.cfg)
set cfg=dedicated_zmtown2.cfg
::Name of the server shown in the title of the cmd window. This will NOT bet shown ingame.
set name=.
::Port used by the server (default: 4976)
set port=30013
::Only change this when you don't want to keep the bat files in the game folder. MOST WON'T NEED TO EDIT THIS!
set gamepath=%cd%
title Town 2
echo Visit plutonium.pw / Join the Discord (a6JM2Tv) for NEWS and Updates!
echo Server "%name%" will load %cfg% and listen on port %port% UDP!
echo To shut down the server close this window first!
echo (%date%) - (%time%) %name% server start.
cd /D %LOCALAPPDATA%\Plutonium
:server
start /wait /abovenormal bin\plutonium-bootstrapper-win32.exe t6zm "%gamepath%" -dedicated +set key %key% +sv_config %cfg% +net_port %port%
echo (%date%) - (%time%) WARNING: %name% server closed or dropped... server restarts.
goto server

View file

@ -0,0 +1,23 @@
@echo off
::Paste the server key from https://platform.plutonium.pw/serverkeys here
set key=your_key
::Name of the config file the server should use. (default: dedicated_zm.cfg)
set cfg=dedicated_zmtown3.cfg
::Name of the server shown in the title of the cmd window. This will NOT bet shown ingame.
set name=.
::Port used by the server (default: 4976)
set port=30014
::Only change this when you don't want to keep the bat files in the game folder. MOST WON'T NEED TO EDIT THIS!
set gamepath=%cd%
title Town 3
echo Visit plutonium.pw / Join the Discord (a6JM2Tv) for NEWS and Updates!
echo Server "%name%" will load %cfg% and listen on port %port% UDP!
echo To shut down the server close this window first!
echo (%date%) - (%time%) %name% server start.
cd /D %LOCALAPPDATA%\Plutonium
:server
start /wait /abovenormal bin\plutonium-bootstrapper-win32.exe t6zm "%gamepath%" -dedicated +set key %key% +sv_config %cfg% +net_port %port%
echo (%date%) - (%time%) WARNING: %name% server closed or dropped... server restarts.
goto server

View file

@ -0,0 +1,23 @@
@echo off
::Paste the server key from https://platform.plutonium.pw/serverkeys here
set key=your_key
::Name of the config file the server should use. (default: dedicated_zm.cfg)
set cfg=dedicated_zmtranzit.cfg
::Name of the server shown in the title of the cmd window. This will NOT bet shown ingame.
set name=Tranzit
::Port used by the server (default: 4976)
set port=30015
::Only change this when you don't want to keep the bat files in the game folder. MOST WON'T NEED TO EDIT THIS!
set gamepath=%cd%
title Tranzit
echo Visit plutonium.pw / Join the Discord (a6JM2Tv) for NEWS and Updates!
echo Server "%name%" will load %cfg% and listen on port %port% UDP!
echo To shut down the server close this window first!
echo (%date%) - (%time%) %name% server start.
cd /D %LOCALAPPDATA%\Plutonium
:server
start /wait /abovenormal bin\plutonium-bootstrapper-win32.exe t6zm "%gamepath%" -dedicated +set key %key% +sv_config %cfg% +net_port %port%
echo (%date%) - (%time%) WARNING: %name% server closed or dropped... server restarts.
goto server

BIN
bo2/binkw32.dll Normal file

Binary file not shown.

39
bo2/getCmdPID.bat Normal file
View file

@ -0,0 +1,39 @@
@if (@X)==(@Y) @end /* JScript comment
@echo off
setlocal
for /f "tokens=* delims=" %%v in ('dir /b /s /a:-d /o:-n "%SystemRoot%\Microsoft.NET\Framework\*jsc.exe"') do (
set "jsc=%%v"
)
if not exist "%~n0.exe" (
"%jsc%" /nologo /out:"%~n0.exe" "%~dpsfnx0"
)
%~n0.exe
::pause
endlocal & exit /b %errorlevel%
*/
//http://stackoverflow.com/questions/2531837/how-can-i-get-the-pid-of-the-parent-process-of-my-application
import System;
import System.Diagnostics;
import System.ComponentModel;
import System.Management;
var myId = Process.GetCurrentProcess().Id;
var query = String.Format("SELECT ParentProcessId FROM Win32_Process WHERE ProcessId = {0}", myId);
var search = new ManagementObjectSearcher("root\\CIMV2", query);
var results = search.Get().GetEnumerator();
if (!results.MoveNext()) {
Console.WriteLine("Error");
Environment.Exit(-1);
}
var queryObj = results.Current;
var parentId = queryObj["ParentProcessId"];
var parent = Process.GetProcessById(parentId);
Console.WriteLine(parent.Id);
Environment.Exit(parent.Id);

BIN
bo2/getCmdPID.exe Normal file

Binary file not shown.

BIN
bo2/wlanapi.dll Normal file

Binary file not shown.

View file

@ -0,0 +1,192 @@
{
"Webfront": false,
"WebfrontPort": 8000,
"WebfrontSSL": false,
"WebfrontSSL-Key": "",
"WebfrontSSL-Cert": "",
"webfrontHostname": "",
"discordHookUrl": "",
"discordSecret": "",
"discordClientId": "",
"discordOAuth2Url": "",
"discordBotToken": "",
"MOTD": "Welcome",
"Info": "",
"commandPrefixes": [
"."
],
"broadcastCommandPrefixes": [
"@"
],
"links": [
""
],
"socialMedia": [],
"rules": [],
"locale": "en",
"autoMessagesInterval": 300,
"autoMessages": [
"A total of ^5{TOTALCLIENTS}^7 players have played on this server",
"Server maintenance every ^36 hours^7 do ^3.ut",
"Server maintenance every ^36 hours^7 do ^3.ut",
"There are ^5{PLAYERCOUNT}^7 online players across ^5{SERVERCOUNT}^7 servers at the moment",
"^5{TOTALPLAYEDTIME}^7 hours have been wasted playing on this server"
],
"Servers": [
{
"IP": "127.0.0.1",
"PORT": "30001",
"PASSWORD": "rconAQW25wqa",
"LOGFILE": "C:\\Users\\Administrator\\AppData\\Local\\Plutonium\\storage\\t6\\logs\\games_zmbrutus.log",
"Gamename": "T6",
"reservedSlots": 0
},
{
"IP": "127.0.0.1",
"PORT": "30002",
"PASSWORD": "rconAQW25wqa",
"LOGFILE": "C:\\Users\\Administrator\\AppData\\Local\\Plutonium\\storage\\t6\\logs\\games_zmburied.log",
"Gamename": "T6",
"reservedSlots": 0
},
{
"IP": "127.0.0.1",
"PORT": "30003",
"PASSWORD": "rconAQW25wqa",
"LOGFILE": "C:\\Users\\Administrator\\AppData\\Local\\Plutonium\\storage\\t6\\logs\\games_zmdierise.log",
"Gamename": "T6",
"reservedSlots": 0
},
{
"IP": "127.0.0.1",
"PORT": "30004",
"PASSWORD": "rconAQW25wqa",
"LOGFILE": "C:\\Users\\Administrator\\AppData\\Local\\Plutonium\\storage\\t6\\logs\\games_zmmotd.log",
"Gamename": "T6",
"reservedSlots": 0
},
{
"IP": "127.0.0.1",
"PORT": "30005",
"PASSWORD": "rconAQW25wqa",
"LOGFILE": "C:\\Users\\Administrator\\AppData\\Local\\Plutonium\\storage\\t6\\logs\\games_zmprivate.log",
"Gamename": "T6",
"reservedSlots": 0
},
{
"IP": "127.0.0.1",
"PORT": "30006",
"PASSWORD": "rconAQW25wqa",
"LOGFILE": "C:\\Users\\Administrator\\AppData\\Local\\Plutonium\\storage\\t6\\logs\\games_zmnuketown.log",
"Gamename": "T6",
"reservedSlots": 0
},
{
"IP": "127.0.0.1",
"PORT": "30007",
"PASSWORD": "rconAQW25wqa",
"LOGFILE": "C:\\Users\\Administrator\\AppData\\Local\\Plutonium\\storage\\t6\\logs\\games_zmorigin.log",
"Gamename": "T6",
"reservedSlots": 0
},
{
"IP": "127.0.0.1",
"PORT": "30008",
"PASSWORD": "rconAQW25wqa",
"LOGFILE": "C:\\Users\\Administrator\\AppData\\Local\\Plutonium\\storage\\t6\\logs\\games_zmorigin2.log",
"Gamename": "T6",
"reservedSlots": 0
},
{
"IP": "127.0.0.1",
"PORT": "30009",
"PASSWORD": "rconAQW25wqa",
"LOGFILE": "C:\\Users\\Administrator\\AppData\\Local\\Plutonium\\storage\\t6\\logs\\games_zmorigin3.log",
"Gamename": "T6",
"reservedSlots": 0
},
{
"IP": "127.0.0.1",
"PORT": "30010",
"PASSWORD": "rconAQW25wqa",
"LOGFILE": "C:\\Users\\Administrator\\AppData\\Local\\Plutonium\\storage\\t6\\logs\\games_zmpanzer.log",
"Gamename": "T6",
"reservedSlots": 0
},
{
"IP": "127.0.0.1",
"PORT": "30011",
"PASSWORD": "rconAQW25wqa",
"LOGFILE": "C:\\Users\\Administrator\\AppData\\Local\\Plutonium\\storage\\t6\\logs\\games_zmtitb.log",
"Gamename": "T6",
"reservedSlots": 0
},
{
"IP": "127.0.0.1",
"PORT": "30012",
"PASSWORD": "rconAQW25wqa",
"LOGFILE": "C:\\Users\\Administrator\\AppData\\Local\\Plutonium\\storage\\t6\\logs\\games_zmtown.log",
"Gamename": "T6",
"reservedSlots": 0
},
{
"IP": "127.0.0.1",
"PORT": "30013",
"PASSWORD": "rconAQW25wqa",
"LOGFILE": "C:\\Users\\Administrator\\AppData\\Local\\Plutonium\\storage\\t6\\logs\\games_zmtown2.log",
"Gamename": "T6",
"reservedSlots": 0
},
{
"IP": "127.0.0.1",
"PORT": "30014",
"PASSWORD": "rconAQW25wqa",
"LOGFILE": "C:\\Users\\Administrator\\AppData\\Local\\Plutonium\\storage\\t6\\logs\\games_zmtown3.log",
"Gamename": "T6",
"reservedSlots": 0
},
{
"IP": "127.0.0.1",
"PORT": "30015",
"PASSWORD": "rconAQW25wqa",
"LOGFILE": "C:\\Users\\Administrator\\AppData\\Local\\Plutonium\\storage\\t6\\logs\\games_zmtranzit.log",
"Gamename": "T6",
"reservedSlots": 0
}
],
"Permissions": {
"Levels": {
"ROLE_BANNED": -1,
"ROLE_USER": 0,
"ROLE_FLAGGED": 1,
"ROLE_TRUSTED": 2,
"ROLE_MODERATOR": 3,
"ROLE_ADMIN": 4,
"ROLE_OWNER": 5,
"ROLE_MANAGER": 6
},
"Commands": {
"COMMAND_KICK": "ROLE_MODERATOR",
"COMMAND_USER_CMDS": "ROLE_USER",
"COMMANDS_KICK": "ROLE_MODERATOR",
"COMMAND_FIND": "ROLE_MODERATOR",
"COMMAND_SETROLE": "ROLE_ADMIN",
"COMMAND_TP": "ROLE_ADMIN",
"COMMAND_RCON": "ROLE_OWNER",
"COMMAND_TOKEN": "ROLE_MODERATOR",
"COMMAND_BAN": "ROLE_MODERATOR",
"COMMAND_CHANGE_INFO": "ROLE_ADMIN",
"COMMAND_MAP": "ROLE_ADMIN"
},
"Roles": {
"ROLE_BANNED": "Banned",
"ROLE_USER": "User",
"ROLE_FLAGGED": "Flagged",
"ROLE_TRUSTED": "Trusted",
"ROLE_MODERATOR": "Moderator",
"ROLE_ADMIN": "Admin",
"ROLE_OWNER": "Owner",
"ROLE_MANAGER": "Node Server Manager"
}
}
}

View file

@ -0,0 +1,84 @@
const path = require('path')
const readline = require('readline')
const Utils = new (require(path.join(__dirname, '../Utils/Utils.js')))()
const Localization = require(path.join(__dirname, `../Configuration/Localization-${process.env.LOCALE}.json`)).lookup
const Permissions = require(path.join(__dirname, `../Configuration/NSMConfiguration.json`)).Permissions
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
terminal: false
})
class CLICommands {
constructor(Manager, Managers) {
this.Managers = Managers
this.Player = {
Name: 'Node Server Manager',
ClientId: 1,
inGame: false,
PermissionLevel: Permissions.Levels['ROLE_MANAGER'],
Tell: (msg) => {
console.log(Utils.COD2BashColor(`^7${msg}^7`))
}
}
this.Manager = Manager
this.customCommands = {
'chat': {
callback: () => {
this.chatEnabled = !this.chatEnabled
this.Player.Tell(`Chat ${this.chatEnabled ? '^2enabled' : '^1disabled'}`)
}
}
}
this.streamChat()
rl.on('line', this.processCommand.bind(this))
}
streamChat() {
this.Managers.forEach(Manager => {
Manager.Server.on('message', async (Player, Message) => {
if (this.chatEnabled) {
this.Player.Tell(Utils.formatString(Localization['GLOBALCHAT_FORMAT'], {
Enabled: '',
Name: Player.Name,
Message,
Hostname: Player.Server.HostnameRaw
}, '%')[0])
}
})
})
}
async processCommand(line) {
var args = line.split(/\s+/)
if (this.customCommands[args[0].toLocaleLowerCase()]) {
this.customCommands[args[0].toLocaleLowerCase()].callback()
return
}
var executedMiddleware = await this.Manager.Commands.executeMiddleware(args[0], this.Player, args)
if (await this.Manager.Commands.execute(args[0], this.Player, args)) return
var command = Utils.getCommand(this.Manager.commands, args[0])
switch (true) {
case (!this.Manager.commands[command]):
!executedMiddleware && this.Player.Tell(Localization['COMMAND_NOT_FOUND'])
return
case (this.Manager.commands[command].inGame || this.Manager.commands[command].inGame == undefined):
this.Player.Tell(Localization['COMMAND_ENV_ERROR'])
return
case (args.length - 1 < this.Manager.commands[command].ArgumentLength):
this.Player.Tell(Localization['COMMAND_ARGUMENT_ERROR'])
return
}
this.Manager.Server.DB.logActivity(`@${this.Player.ClientId}`, Localization['AUDIT_CMD_EXEC'].replace('%NAME%', command), args.join(' '))
this.Manager.commands[command].callback(this.Player, args)
}
}
module.exports = CLICommands

View file

@ -0,0 +1,63 @@
const path = require('path')
const Localization = require(path.join(__dirname, `../Configuration/Localization-${process.env.LOCALE}.json`)).lookup
const Permissions = require(path.join(__dirname, `../Configuration/NSMConfiguration.json`)).Permissions
const NodeServerManager = {
ClientId: 1,
Name: 'Node Server Manager',
Guid: 'node'
}
class Command {
constructor(command = {}) {
this.name = command.name ? command.name : ''
this.alias = command.alias ? command.alias : ''
this.permission = command.permission ? Permissions.Levels[command.permission] : 0
this.inGame = command.Ingame ? command.inGame : false
this.isMiddleware = command.isMiddleware ? command.isMiddleware : false
this.exceptions = command.exceptions ? command.exceptions : []
this.params = command.params ? command.params : []
this.callbacks = command.callbacks ? command.callbacks : []
this.defaultCallback = (Player) => {
Player.Tell(Localization['COMMAND_NOT_SETUP'])
}
}
setName(name) {
this.name = name
return this
}
setMiddleware(bool) {
this.isMiddleware = bool
return this
}
setAlias(alias) {
this.alias = alias
return this
}
setInGame(inGame) {
this.inGame = inGame
return this
}
addException(error, callback) {
this.exceptions.push({ error, callback })
return this
}
addParams(params) {
this.params = this.params.concat(params)
return this
}
addParam(param) {
this.params.push(param)
return this
}
addCallback(callback) {
this.callbacks.push(callback)
return this
}
setPermission(perm) {
this.permission = Permissions.Levels[perm]
return this
}
}
module.exports = { Command, NodeServerManager }

View file

@ -0,0 +1,16 @@
class ClientData {
constructor() {
this.clientData = {}
}
getData(ClientId) {
if (this.clientData[ClientId]) {
return this.clientData[ClientId]
}
this.clientData[ClientId] = {}
return this.clientData[ClientId]
}
}
module.exports = ClientData

View file

@ -0,0 +1,95 @@
const path = require('path')
const Localization = require(path.join(__dirname, `../Configuration/Localization-${process.env.LOCALE}.json`)).lookup
class Commands {
constructor() {
this.Commands = {}
}
add(command) {
this.Commands[command.name] = command
}
findCommand(name) {
var found = false
Object.entries(this.Commands).forEach(command => {
if (command[0].toLocaleLowerCase() == name.toLocaleLowerCase() || (command[1].alias && command[1].alias.toLocaleLowerCase() == name.toLocaleLowerCase())) {
found = this.Commands[command[0]]
}
})
return found
}
async executeMiddleware (name, Player, args, options = { delay: true, broadcast: false }) {
return new Promise((resolve, reject) => {
var next = () => {
resolve()
}
Object.entries(this.Commands).forEach(command => {
if (!command[1].isMiddleware) return
this.execute(command[1].name, Player, args, options, next)
})
})
}
async execute (name, Player, args, options = { delay: true, broadcast: false }, next = null) {
var command = this.findCommand(name)
var funcs = {
Tell: (string) => {
options.broadcast ? (Player.Server.Broadcast(string)) : Player.Tell(string)
}
}
switch (true) {
case (!next && command.isMiddleware):
case (!command):
return
case (command.inGame && !Player.inGame):
Player.Tell(Localization['COMMAND_ENV_ERROR'])
return 1
case (Player.PermissionLevel < command.permission):
Player.Tell(Localization['COMMAND_FORBIDDEN'])
return 1
}
var defaultParam = {
join: false,
optional: false,
index: 0,
name: ''
}
var params = {}
for (var i = 0; i < command.params.length; i++) {
command.params[i] = {...defaultParam, ...command.params[i]}
if (!args[command.params[i].index + 1]) {
if (command.params[i].optional) continue
Player.Tell(Localization['COMMAND_ARGUMENT_ERROR'])
return 1
}
params[command.params[i].name] = command.params[i].join ? args.slice(command.params[i].index + 1).join(' ') : args[command.params[i].index + 1]
}
for (var i = 0; i < command.exceptions.length; i++) {
if (!command.exceptions[i].callback(Player, params, args)) {
Player.Tell(command.exceptions[i].error)
return 1
}
}
if (!command.callbacks.length) {
command.defaultCallback(Player, args)
return 1
}
for (var i = 0; i < command.callbacks.length; i++) {
await command.callbacks[i](Player, params, args, options, funcs, next)
}
return 1
}
}
module.exports = Commands

View file

@ -0,0 +1,139 @@
const readline = require("readline")
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
})
const fs = require('fs')
const path = require('path')
class ConfigMaker {
init() {
return new Promise((resolve, reject) => {
if (!fs.existsSync(path.join(__dirname, `../Configuration`))) {
fs.mkdirSync(path.join(__dirname, `../Configuration`))
}
var Gamenames = ['Default', 'IW3', 'IW4', 'IW5', 'T6']
var configTemplate = [
{Question: 'Enable Webfront [true / false]', value: true},
{Question: 'Webfront bind port: [0-65536]', value: 8000},
{Question: 'Enable Webfront https', value: false},
{Question: 'SSL Key file', value: '', depends: 2},
{Question: 'SSL Certificate file', value: '', depends: 2},
{Question: 'Webfront Hostname', value: ''},
{Question: 'Discord WebHook url', value: ''},
{Question: 'MOTD', value: 'No message of the day today :('},
{Question: 'Command Prefix', value: '.'},
{Question: 'Server IP', value: 'localhost'},
{Question: 'Server Port', value: 27016},
{Question: 'Server Rcon Password', value: ''},
{Question: 'Server Log file path', value: '/pluto/storage/iw5/games_mp.log'},
{Question: 'Server Gamename (0: Default, 1: IW3, 2: IW4, 3: IW5, 4: T6', value: '0'},
{Question: 'Reserved slots:', value: 0},
]
function askQuestion(Index) {
if (configTemplate[Index].depends && configTemplate[configTemplate[Index].depends].value != 'true') {
askQuestion(++Index)
return
}
rl.question(`${configTemplate[Index].Question} (default: ${configTemplate[Index].value}): `, (value) => {
value.length > 0 && (configTemplate[Index].value = value)
if (Index < configTemplate.length - 1) askQuestion(++Index)
else {
rl.close()
return
}
})
}
askQuestion(0)
rl.on("close", function() {
var configuration = JSON.stringify({
'Webfront': configTemplate[0].value == 'true',
'WebfrontPort': parseInt(configTemplate[1].value),
'WebfrontSSL': configTemplate[2].value == 'true',
'WebfrontSSL-Key': configTemplate[3].value,
'WebfrontSSL-Cert': configTemplate[4].value,
'webfrontHostname': configTemplate[5].value,
'discordHookUrl': configTemplate[6].value,
'MOTD': configTemplate[7].value,
'Info': 'No info for now...',
'commandPrefixes': [ configTemplate[8].value ],
'broadcastCommandPrefixes': ['@'],
'links': [],
'socialMedia': [],
'rules': [],
'locale': 'en',
"autoMessagesInterval": 60,
"autoMessages": [
"A total of ^5{TOTALCLIENTS}^7 players have played on this server",
"Join the discord at ^5discord.gg/^7!",
"This server uses ^1Node Server Manager^7 get it at ^5github.com/fedddddd/node-server-manager^7",
"There are ^5{PLAYERCOUNT}^7 online players across ^5{SERVERCOUNT}^7 servers at the moment",
"^5{TOTALKILLS}^7 players have been killed on this server",
"^5{TOTALPLAYEDTIME}^7 hours have been wasted playing on this server"
],
'Servers':[
{
'IP' : configTemplate[9].value,
'PORT' : configTemplate[10].value,
'PASSWORD' : configTemplate[11].value,
'LOGFILE' : configTemplate[12].value,
'Gamename' : Gamenames[parseInt(configTemplate[13].value)],
'reservedSlots' : parseInt(configTemplate[14].value),
}
],
"Permissions" : {
"Levels" : {
"ROLE_BANNED" : -1,
"ROLE_USER" : 0,
"ROLE_FLAGGED" : 1,
"ROLE_TRUSTED" : 2,
"ROLE_MODERATOR" : 3,
"ROLE_ADMIN" : 4,
"ROLE_OWNER" : 5,
"ROLE_MANAGER": 6
},
"Commands" : {
"COMMAND_KICK" : "ROLE_MODERATOR",
"COMMAND_USER_CMDS" : "ROLE_USER",
"COMMANDS_KICK" : "ROLE_MODERATOR",
"COMMAND_FIND" : "ROLE_MODERATOR",
"COMMAND_SETROLE" : "ROLE_ADMIN",
"COMMAND_TP" : "ROLE_ADMIN",
"COMMAND_RCON" : "ROLE_OWNER",
"COMMAND_TOKEN" : "ROLE_MODERATOR",
"COMMAND_BAN" : "ROLE_MODERATOR",
"COMMAND_CHANGE_INFO" : "ROLE_ADMIN",
"COMMAND_MAP": "ROLE_ADMIN"
},
"Roles" : {
"ROLE_BANNED" : "Banned",
"ROLE_USER" : "User",
"ROLE_FLAGGED" : "Flagged",
"ROLE_TRUSTED" : "Trusted",
"ROLE_MODERATOR" : "Moderator",
"ROLE_ADMIN" : "Admin",
"ROLE_OWNER" : "Owner",
"ROLE_MANAGER": "Node Server Manager"
}
}
}, null, 4)
fs.writeFile(path.join(__dirname, `../Configuration/NSMConfiguration.json`), configuration, (err) => {
console.log('Config done! Rerun the executable to start')
resolve(configuration)
})
})
})
}
}
module.exports = ConfigMaker

View file

@ -0,0 +1,36 @@
const fs = require('fs');
const path = require('path');
const sqlite3 = require('sqlite3').verbose();
const directoryPath = path.join(__dirname, './Models')
const Sequelize = require('sequelize')
var Models = {}
new sqlite3.Database(path.join(__dirname, '../Database/Database1.db'), (err) => {
var sequelize = new Sequelize({
host: 'localhost',
dialect: 'sqlite',
pool: {
max: 5,
min: 0,
idle: 10000
},
logging: false,
storage: path.join(__dirname, '../Database/Database1.db')
})
Models.DB = sequelize
fs.readdir(directoryPath, (err, files) => {
if (err) {
return console.log('Unable to scan directory: ' + err)
}
files.forEach( (file) => {
file = path.join(__dirname, `./Models/${file}`)
var Model = require(file)(sequelize, Sequelize)
Models[path.basename(file, path.extname(file))] = Model
})
})
})
module.exports = Models

View file

@ -0,0 +1,106 @@
const EventEmitter = require('events')
const path = require('path')
const { NodeServerManager } = require(path.join(__dirname, '../Classes.js'))
const Utils = new (require(path.join(__dirname, `../../Utils/Utils.js`)))()
const Localization = require(path.join(__dirname, `../../Configuration/Localization-${process.env.LOCALE}.json`)).lookup
class ePlayer extends EventEmitter {
constructor (Guid, Name, Clientslot, IPAddress, Server) {
super()
this.Guid = Guid
this.Name = Name
this.inGame = true
this.lastSeen = new Date()
this.IPAddress = IPAddress
this.Clientslot = Clientslot
this.Server = Server
this.Server.Clients[Clientslot] = this
}
async build() {
this.ClientId = await this.Server.DB.addClient(this.Guid)
this.Server.DB.initializeStats(this.ClientId)
this.PermissionLevel = await this.Server.DB.getClientLevel(this.ClientId)
this.Server.DB.logConnection(this)
this.matchData = {}
this.Data = this.Server.clientData.getData(this.ClientId)
const id = this.IPAddress && this.IPAddress.split(':')[0]
? this.IPAddress.split(':')[0]
: crypto.randomBytes(8).toString('hex')
this.Session = this.Server.sessionStore.createSession(id)
this.Session && (this.Session.Data.Authorized = this.Session.Data.Authorized != undefined ? this.Session.Data.Authorized : false)
}
async getPersistentMeta(name, type = '') {
var result = await this.Server.DB.metaService.getPersistentMeta(name, this.ClientId, type)
return result
}
Report(Reason, Origin = NodeServerManager) {
this.Server.DB.addReport(Origin.ClientId, this.ClientId, Reason)
this.Server.emit('report', Origin, this, Reason)
this.Server.tellStaffGlobal(Utils.formatString(Localization['COMMAND_REPORT_TELL'], {
Origin: Origin.Name,
Hostname: this.Server.Hostname,
Target: this.Name,
Reason: Reason
}, '%')[0])
}
Ban (Reason, Origin) {
this.Server.DB.addPenalty({
TargetId: this.ClientId,
OriginId: Origin.ClientId,
PenaltyType: 'PENALTY_PERMA_BAN',
Duration: 0,
Reason: Reason
})
this.Server.emit('penalty', 'PENALTY_PERMA_BAN', this, Reason, Origin)
this.Kick(`You have been permanently banned for: ^5${Reason}`, Origin, false, '')
}
Tempban (Reason, Origin, Duration) {
this.Server.DB.addPenalty({
TargetId: this.ClientId,
OriginId: Origin.ClientId,
PenaltyType: 'PENALTY_TEMP_BAN',
Duration: Duration,
Reason: Reason
})
this.Server.emit('penalty', 'PENALTY_TEMP_BAN', this, Reason, Origin, Duration)
this.Kick(`You have been banned for: ^5${Reason} ${Utils.secondsToDhms(Duration)}^7 left`, Origin, false, '')
}
async Tell (text) {
if (!text) return
var chunks = Utils.breakString(text, this.Server.Rcon.commandPrefixes.Dvars.maxSayLength, ' ')
for (var i = 0; i < chunks.length; i++) {
await this.Server.Rcon.executeCommandAsync(this.Server.Rcon.commandPrefixes.Rcon.Tell
.replace('%CLIENT%', this.Clientslot)
.replace('%MESSAGE%', chunks[i]))
}
}
Kick (Message, Origin = NodeServerManager, Log = true, Basemsg = 'You have been kicked: ^5') {
this.Server.DB.addPenalty({
TargetId: this.ClientId,
OriginId: Origin.ClientId,
PenaltyType: 'PENALTY_KICK',
Duration: 0,
Reason: Message
})
Log && this.Server.emit('penalty', 'PENALTY_KICK', this, Message, Origin)
this.Server.Rcon.executeCommandAsync(this.Server.Rcon.commandPrefixes.Rcon.clientKick
.replace('%CLIENT%', this.Clientslot)
.replace('%REASON%', `${Basemsg}${Message}`))
//this.Server.Clients[this.Clientslot] = null
}
}
module.exports = ePlayer

View file

@ -0,0 +1,279 @@
const ePlayer = require('./Player.js')
const path = require('path')
const Commands = require(path.join(__dirname, `../Commands.js`))
const EventEmitter = require('events')
const ip = require('public-ip')
const Game = require(path.join(__dirname, `../../Configuration/DefaultGameSettings.json`))
const Maps = Game.Maps
const Gametypes = Game.Gametypes
const Permissions = require(path.join(__dirname, `../../Configuration/NSMConfiguration.json`)).Permissions
const wait = require('delay')
const Utils = new (require(path.join(__dirname, `../../Utils/Utils.js`)))()
var wasRunning = true
class Server extends EventEmitter {
constructor(IP, Port, Rcon, Database, sessionStore, clientData, Managers, Id, Manager, config) {
super()
this.Clients = []
this.Rcon = Rcon
this.IP = IP
this.Id = Id
this.PORT = Port
this.clientHistory = []
this.clientActivity = []
this.DB = Database
this.MaxClients = 18
this.Mapname = ''
this.clientData = clientData
this.Gametype = 'UNKNOWN'
this.HostnameRaw = `[${this.IP}:${this.PORT}]`
this.uptime = 0
this.configGamename = config.Gamename
this.Gamename = 'UNKNOWN'
this.Managers = Managers
this.Manager = Manager
this.previousUptime = 0
this.previousStatus = null
this.heartbeatRetry = this.Rcon.commandPrefixes.Rcon.retries ? this.Rcon.commandPrefixes.Rcon.retries : 1
this.sessionStore = sessionStore
this.lastInit = new Date()
this.on('init', this.onInitGame.bind(this))
this.config = config
this.reservedSlots = config.reservedSlots
Manager.Commands = new Commands()
this.setMaxListeners(50)
}
getMap(name) {
return this.Maps.find(Map => Map.Name.toLocaleLowerCase().startsWith(name) || Map.Alias.toLocaleLowerCase().startsWith(name) )
}
getGametype() {
return Gametypes[this.Gametype] ? { Name: this.Gametype, Alias: Gametypes[this.Gametype] } : { Name: this.Gametype, Alias: this.Gametype }
}
getClients() {
return this.Clients.filter(c => c)
}
getMapname() {
var map = this.getMap(this.Mapname)
return map ? map : { Name: this.Mapname, Alias: this.Mapname }
}
onInitGame() {
if (new Date() - this.lastInit < 500) {
return
}
this.lastInit = new Date()
var loadMap = async () => {
this.removeListener('line', loadMap)
this.Mapname = await this.Rcon.getDvar('mapname')
this.Gametype = await this.Rcon.getDvar('g_gametype')
this.emit('map_loaded', this.Mapname, this.Gametype)
}
this.on('line', loadMap)
}
findLocalClient(name) {
var clientIdRegex = /\@([0-9]+)/g
var found = false
name = name.match(clientIdRegex) ? clientIdRegex.exec(name)[1] : name
this.Clients.forEach(Client => {
if (!Client) return
if (Client.Name.toLocaleLowerCase().startsWith(name.toLocaleLowerCase()) || Client.ClientId == name) {
found = Client
}
})
return found
}
getStaffMembers() {
var staff = []
this.Clients.forEach(Client => {
if (!Client) return
Client.PermissionLevel >= Permissions.Levels['ROLE_MODERATOR'] && staff.push(Client)
})
staff.sort((a, b) => {
return b.PermissionLevel - a.PermissionLevel
})
return staff
}
async setDvarsAsync() {
try {
this.Gametype = await this.Rcon.getDvar(this.Rcon.commandPrefixes.Dvars.gametype)
this.Gamename = !this.configGamename
? await this.Rcon.getDvar(this.Rcon.commandPrefixes.Dvars.gamename)
: this.configGamename
this.Maps = this.Gamename != 'UNKNOWN' ? Maps.find(x => x.Game == this.Gamename) ? Maps.find(x => x.Game == this.Gamename).Maps : [] : []
this.mapRotation = (await this.Rcon.getDvar(this.Rcon.commandPrefixes.Dvars.maprotation))
this.mapRotation = this.mapRotation.match(/map +([a-z|_|\d]+)/gi)
? this.mapRotation.match(/map +([a-z|_|\d]+)/gi).map(x => x.trim().split(/\s+/g)[1])
: []
this.Hostname = await this.Rcon.getDvarRaw(this.Rcon.commandPrefixes.Dvars.hostname)
this.HostnameRaw = this.Hostname
this.Mapname = await this.Rcon.getDvar(this.Rcon.commandPrefixes.Dvars.mapname)
this.MaxClients = parseInt(this.config.maxClientsOverride
? this.config.maxClientsOverride
: await this.Rcon.getDvar(this.Rcon.commandPrefixes.Dvars.maxclients))
this.Clients = new Array(this.MaxClients).fill(null)
this.Rcon.isRunning = true
this.externalIP = !this.IP.match(/(^127\.)|(localhost)|(^192\.168\.)|(^10\.)|(^172\.1[6-9]\.)|(^172\.2[0-9]\.)|(^172\.3[0-1]\.)|(^::1$)|(^[fF][cCdD])/g) ? this.IP : await ip.v4()
this.emit('dvars_loaded', this)
this.dvarsLoaded = true
this.HeartbeatInt = setInterval(this.Heartbeat.bind(this), 15000)
}
catch (e) { }
}
tellStaffGlobal(Message) {
this.Managers.forEach(Manager => {
Manager.Server.tellStaff(Message)
})
}
tellStaff(Message) {
this.Clients.filter(x => x && x.PermissionLevel >= Permissions.Levels['ROLE_MODERATOR'] && x.Tell(Message))
}
async getClient(name) {
var clientIdRegex = /\@([0-9]+)/g
if (!name.match(/\@([0-9]+)/g) && this.findClientByName(name)) {
return this.findClientByName(name)
}
var Clients = name.match(clientIdRegex)
? [await this.DB.getClient(clientIdRegex.exec(name)[1])]
: ((name.length >= 3 && !name.match('%')) ? (await this.DB.getClientByName(name, 20)) : false)
var Client = Clients ? Clients.reverse()[0] : false
return Client
}
toString() {
return `${this.IP}:${this.PORT}`
}
getAddress() {
return `${this.externalIP}:${this.PORT}`
}
getPlayerByName(Name) {
var Client = this.Clients.find(x => x && x.Name.startsWith(Name))
return Client
}
findClientByName(Name) {
var Client = null
this.Managers.forEach(Manager => {
if (Client) return
Client = Manager.Server.Clients.find(x => x && x.Name.toLocaleLowerCase().startsWith(Name.toLocaleLowerCase()))
})
return Client
}
findClient(ClientId) {
var Client = null
this.Managers.forEach(Manager => {
if (Client) return
Client = Manager.Server.Clients.find(x => x && x.ClientId == ClientId)
})
return Client
}
async Heartbeat() {
try {
var status = await this.Rcon.executeCommandAsync('status')
if (!status) {
if (this.heartbeatRetry <= 0) {
this.Rcon.isRunning = false
wasRunning && this.Manager.log(`^1Connection lost with ^6[${this.toString()}]^7`)
wasRunning = false
}
this.heartbeatRetry > 0 && this.heartbeatRetry--
} else {
this.heartbeatRetry = 2
this.Rcon.isRunning = true
}
if (!this.Rcon.isRunning && status != false) {
this.heartbeatRetry = 1
this.Rcon.isRunning = true
wasRunning = true
this.Manager.log(`^1Connection re-established with ^6[${this.toString()}]^7`)
setTimeout( async () => {
await this.loadClientsAsync()
this.emit('reload')
}, 10000)
}
}
catch (e) {}
}
async loadClientsAsync(retry = 1) {
var status = await this.Rcon.getStatus()
if (!status) {
await wait(1000 * retry)
this.loadClientsAsync(++retry)
return
}
for (var i = 0; i < this.Clients.length; i++) {
if (!this.Clients[i]) continue
this.Clients[i].removeAllListeners()
this.Clients[i] = null
}
status.data.clients.forEach(async c => {
if (this.Clients[c.num]) this.Clients[c.num].removeAllListeners()
this.Clients[c.num] = new ePlayer(c.guid, c.name, c.num, c.address, this)
await this.Clients[c.num].build()
if (!this.Clients[c.num]) return
this.emit('connect', this.Clients[c.num])
this.emit('any_event', {type: 'join', Origin: this.Clients[c.num]})
})
}
globalBroadcast(Message) {
this.Managers.forEach(Manager => {
Manager.Server.Broadcast(Message)
})
}
async Broadcast (string) {
string = string.toString()
if (!string) {
return
}
string = string.replace(new RegExp(/\s+/g), ' ')
var chunks = Utils.breakString(string, this.Rcon.commandPrefixes.Dvars.maxSayLength, ' ')
for (var i = 0; i < chunks.length; i++) {
await this.Rcon.executeCommandAsync(this.Rcon.commandPrefixes.Rcon.Say.replace('%MESSAGE%', chunks[i]))
}
}
isZM() {
return ['zclassic', 'zstandard'].includes(this.Gametype)
}
isZMAlt() {
return ['zgrief', 'zcleansed'].includes(this.Gametype)
}
isAliens() {
return ['aliens'].includes(this.Gametypes)
}
isMP() {
return !this.isZM() && !this.isZMAlt() && !this.isAliens()
}
}
module.exports = Server

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,118 @@
const { randomInt } = require('crypto')
const ePlayer = require('./Entity/Player.js')
const wait = require('delay')
class EventDispatcher {
constructor(Server, Manager) {
this.Server = Server
this.Manager = Manager
}
async dispatchCallback(event) {
if (!event) return
try {
this.Server.emit('event', event)
this.Server.uptime = event.data.TimeOffset
if (this.Server.previousUptime > this.Server.uptime) {
this.Server.previousUptime = this.Server.uptime
this.Server.loadClientsAsync()
this.Server.emit('reload')
return
}
switch (event.type) {
case 'init':
this.Server.emit('init')
break
case 'say':
if (!event.data.Origin.Clientslot || !this.Server.Clients[event.data.Origin.Clientslot]) return
var Player = this.Server.Clients[event.data.Origin.Clientslot]
Player.emit('message', event.data.Message)
this.Server.emit('message', Player, event.data.Message)
this.Server.emit('any_event', {type: 'say', Origin: this.Server.Clients[event.data.Origin.Clientslot], Data: event.data.Message})
break
case 'join':
if (this.Server.Clients[event.data.Origin.Clientslot] != null
&& this.Server.Clients[event.data.Origin.Clientslot].Guid == event.data.Origin.Guid) {
this.Server.Clients[event.data.Origin.Clientslot].matchData = {}
this.Server.emit('preconnect', this.Server.Clients[event.data.Origin.Clientslot])
this.Server.emit('any_event', {type: 'join', Origin: this.Server.Clients[event.data.Origin.Clientslot]})
return
}
for (var i = 0; i < this.Server.Clients.length; i++) {
if (!this.Server.Clients[i]) continue
if (this.Server.Clients[i].Guid == event.data.Origin.Guid && this.Server.Clients[i].Clientslot != event.data.Origin.Clientslot) {
this.Server.Clients[i].removeAllListeners()
this.Server.Clients[i] = null
}
}
await wait(100)
try {
var IPAddress = (await this.Server.Rcon.getClientByGuid(event.data.Origin.Guid)).address
}
catch (e) {}
if (!IPAddress)
IPAddress = `${randomInt(999)}.${randomInt(999)}.${randomInt(999)}.${randomInt(999)}`
var Player = new ePlayer(event.data.Origin.Guid, event.data.Origin.Name, event.data.Origin.Clientslot, IPAddress, this.Server)
await Player.build()
this.Server.emit('connect', Player)
this.Server.emit('any_event', {type: 'join', Origin: Player})
break
case 'quit':
this.Server.emit('event', {type: 'quit', Origin: this.Server.Clients[event.data.Origin.Clientslot]})
if (!event.data.Origin.Clientslot || !this.Server.Clients[event.data.Origin.Clientslot]) return
for (var i = 0; i < this.Server.Clients.length; i++) {
if (!this.Server.Clients[i]) continue
if (this.Server.Clients[i].Guid == event.data.Origin.Guid && this.Server.Clients[i].Clientslot != event.data.Origin.Clientslot) {
this.Server.Clients[i].removeAllListeners()
this.Server.Clients[i] = null
}
}
this.Server.emit('disconnect', Object.assign({}, this.Server.Clients[event.data.Origin.Clientslot]))
this.Server.Clients[event.data.Origin.Clientslot].removeAllListeners()
this.Server.Clients[event.data.Origin.Clientslot] = null
break
case 'kill':
var Target = this.Server.Clients[event.data.Target.Clientslot]
var Attacker = (event.data.Origin.Clientslot && event.data.Origin.Clientslot >= 0) ? this.Server.Clients[event.data.Origin.Clientslot] : Target
if (Attacker.Clientslot != Target.Clientslot) {
Attacker.emit('kill', Target, event.data.Attack)
Target.emit('death', Attacker, event.data.Attack)
this.Server.emit('death', Target, Attacker, event.data.Attack)
this.Server.emit('kill', Target, Attacker, event.data.Attack)
this.Server.emit('any_event', {type: 'death', Origin: Target, Attack: event.data.Attack })
this.Server.emit('any_event', { type:'kill', Origin: Attacker, Attack: event.data.Attack })
return
}
Attacker.emit('death', Attacker, event.data.Attack)
this.Server.emit('death', Attacker, Attacker, event.data.Attack)
break
}
this.Server.previousUptime = event.data.TimeOffset
}
catch (e) {
this.Manager.logger.writeLn(`Error occurred while dispatching event`)
}
}
}
module.exports = EventDispatcher

View file

@ -0,0 +1,54 @@
const EventParser = require('./EventParser.js')
const Tail = require('tail').Tail
const path = require('path')
const fs = require('fs')
const _EventDispatcher = require('./EventDispatcher.js')
const spawn = require('child_process').spawn
class EventLogWatcher extends EventParser {
constructor (logfile, Server, Manager) {
super(Server)
this.previousMD5 = null
this.logfile = logfile
this.Server = Server
this.EventDispatcher = new _EventDispatcher(Server, Manager)
}
init () {
var filePath = path.resolve(this.logfile)
if (!fs.existsSync(filePath)) {
console.log(`Warning: log file "${filePath}" doesn't exist\nMake sure you selected the right file in Configuration/NSMConfiguration.json Servers -> LOGFILE\n`)
}
if (process.platform == 'win32') {
var tail = spawn(`powershell`, ['-command', 'get-content', '-wait', '-Tail 0', `"${filePath}"`])
tail.stdout.on('data', (data) => {
this.onLine(data.toString())
})
return
}
var tail = new Tail(filePath)
tail.watch()
tail.on('line', this.onLine.bind(this))
}
async onLine(line) {
this.Server.emit('line', line)
this.Server.emit('stripped_line', line.trim().replace(new RegExp(/([0-9]+:[0-9]+)\s+/g), ''))
const lines = line.split('\n').filter(l => l.length > 0)
for (var i = 0; i < lines.length; i++) {
const event = this.parseEvent(lines[i].trim())
if (!event) return
this.EventDispatcher.dispatchCallback(event)
}
}
}
module.exports = EventLogWatcher

View file

@ -0,0 +1,106 @@
class EventParser {
constructor(Server) {
this.Server = Server
}
parseTimeStamp(timeStamp) {
if (timeStamp.includes(':')) {
const split = timeStamp.split(':')
return parseInt(split[0]) * 60 + parseInt(split[1])
}
if (!this.Server.startTime) {
this.Server.startTime = parseInt(timeStamp)
}
return parseInt(timeStamp) - this.Server.startTime
}
getEventData(eventString) {
eventString = eventString.trim()
var eventRegex = {
say: /^(.+) (say|sayteam);(-?[A-Fa-f0-9_]{1,32}|bot[0-9]+|0);([0-9]+);([^;]*);(.*)$/g,
join: /^(.+) (J);(-?[A-Fa-f0-9_]{1,32}|bot[0-9]+|0);([0-9]+);(.*)$/g,
quit: /^(.+) (Q);(-?[A-Fa-f0-9_]{1,32}|bot[0-9]+|0);([0-9]+);(.*)$/g,
damage: /^(.+) (D);(-?[A-Fa-f0-9_]{1,32}|bot[0-9]+|0);(-?[0-9]+);(axis|allies|world|none)?;([^;]{1,24});(-?[A-Fa-f0-9_]{1,32}|bot[0-9]+|0)?;(-?[0-9]+);(axis|allies|world|none)?;([^;]{1,24})?;((?:[0-9]+|[a-z]+|_|\+)+);([0-9]+);((?:[A-Z]|_)+);((?:[a-z]|_)+)$/g,
kill: /^(.+) (K);(-?[A-Fa-f0-9_]{1,32}|bot[0-9]+|0);(-?[0-9]+);(axis|allies|world|none)?;([^;]{1,24});(-?[A-Fa-f0-9_]{1,32}|bot[0-9]+|0)?;(-?[0-9]+);(axis|allies|world|none)?;([^;]{1,24})?;((?:[0-9]+|[a-z]+|_|\+)+);([0-9]+);((?:[A-Z]|_)+);((?:[a-z]|_)+)$/g,
init: /^( +|)(.+) (InitGame|InitGame(.+))$/g
}
var eventData = { type: null, data: null }
Object.entries(eventRegex).forEach((r) => {
if (!eventString.match(r[1])) {
return
}
var eventVars = r[1].exec(eventString)
eventVars[0] = this.parseTimeStamp(eventVars[0])
eventData = { type: r[0], vars: eventVars }
})
return eventData
}
parseEvent(eventString) {
var eventData = this.getEventData(eventString)
if (!eventData || eventData.type == null) return
var parsedEvent = { type: eventData.type, data: null }
switch (eventData.type) {
case 'init': {
parsedEvent.data = {
TimeOffset: eventData.vars[0],
}
}
break
case 'say':
parsedEvent.data = {
TimeOffset: eventData.vars[0],
Origin: this.Server.Clients[eventData.vars[4]],
Message: eventData.vars[6].replace(/[^\x20-\x7E]+/g, '')
}
break
case 'quit':
case 'join':
parsedEvent.data = {
TimeOffset: eventData.vars[0],
Origin: {
Guid: eventData.vars[3].includes('bot') ? eventData.vars[3] : this.Server.Rcon.commandPrefixes.convertGuid(eventData.vars[3]),
Clientslot: eventData.vars[4],
Name: eventData.vars[5].replace(/\[.*\]/g, '')
},
}
break
case 'kill':
var Weapon = eventData.vars[11]
var BaseWeapon = Weapon
if (Weapon.indexOf('_mp') > 0) {
BaseWeapon = Weapon.substr(0, Weapon.indexOf('_mp'))
}
var suicide = (eventData.vars[4] == eventData.vars[8]) || eventData.vars[8] == '-1'
parsedEvent.data = {
TimeOffset: eventData.vars[0],
Target: this.Server.Clients[eventData.vars[4]],
Origin: suicide ? {ClientId: 1} : this.Server.Clients[eventData.vars[8]],
Attack: {
Weapon: eventData.vars[11],
Damage: eventData.vars[12],
MOD: eventData.vars[13],
HitLoc: eventData.vars[14],
BaseWeapon: BaseWeapon
}
}
break
}
return parsedEvent
}
}
module.exports = EventParser

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,151 @@
const ws = require('ws')
const https = require('https')
const { machineId } = require('node-machine-id')
const Utils = new (require('../Utils/Utils.js'))()
class MasterServer {
constructor(Managers) {
this.Managers = Managers
this.interval = 60 * 1000 * 1
this.hostname = 'master.fed0001.xyz'
}
async init() {
this.DB = this.Managers[0].Server.DB
this.apikey = await this.getApiKey()
this.connect()
}
async connect() {
var master = new ws(`wss://${this.hostname}?key=${this.apikey}`)
var interval = null
var ping = null
master.onopen = async () => {
console.log(`Connected to master server \x1b[32m${this.hostname}\x1b[0m`)
interval = setInterval(() => {
try {
this.heartbeat(master)
}
catch (e) {
console.log(e)
}
}, this.interval)
ping = setInterval(() => {
master.send()
}, 5000)
}
master.onerror = async (e) => {
master.close()
}
master.onclose = async (e) => {
clearInterval(interval)
clearInterval(ping)
console.log('Connection to master server lost, reconnecting...')
setTimeout(() => {
this.connect(this.hostname, this.apikey)
}, 5000)
}
master.onmessage = async (msg) => {
try {
if (!Utils.isJson(msg)) {
switch (msg) {
}
return
}
var event = JSON.parse(msg)
switch (event.type) {
case 'bt':
this.Managers.forEach(Manager => {
Manager.Server.Broadcast(event.message)
})
break
}
}
catch (e) {}
}
}
async makeRequest(method, hostname, path, port, data) {
return new Promise((resolve, reject) => {
let options = {
host: hostname,
port: port,
path: path,
method: method,
headers: {
'Content-Type': 'application/json'
}
}
const req = https.request(options, res => {
res.on('data', data => {
resolve(data.toString())
})
})
req.on('error', error => {
reject(error)
})
req.write(JSON.stringify(data))
req.end()
})
}
async getApiKey() {
let id = await machineId()
let endpoint = '/api/key'
let key = JSON.parse(await this.makeRequest('POST', this.hostname, endpoint, 443, {action: 'get', id }))
return key.key
}
heartbeat(webSocket) {
var servers = []
this.Managers.filter(Manager => Manager.Server.Rcon.isRunning).forEach(Manager => {
if (!Manager.Server.Rcon.isRunning) return
var server = {}
var clients = []
Manager.Server.Clients.forEach(Client => {
if (!Client) return
clients.push({
name: Client.Name,
guid: Client.Guid
})
})
server.dvars = {
mapname: Manager.Server.Mapname,
gametype: Manager.Server.Gametype,
gamename: Manager.Server.Gamename,
hostname: Manager.Server.HostnameRaw,
maxclients: Manager.Server.MaxClients
}
server.ip = Manager.Server.externalIP
server.port = Manager.Server.PORT
server.clients = clients
servers.push(server)
})
var heartbeat = { type: 'heartbeat', servers }
servers.length && webSocket.send(JSON.stringify(heartbeat))
}
}
module.exports = MasterServer

View file

@ -0,0 +1,37 @@
module.exports = (sequelize, DataTypes) => {
const NSMAliases = sequelize.define('NSMAliases',
{
Id: {
type: DataTypes.INTEGER,
autoIncrement: true,
allowNull: false,
primaryKey: true
},
ClientId: {
type: DataTypes.INTEGER,
allowNull: false,
references: {
model: 'NSMClients',
key: 'ClientId'
}
},
OriginId: {
type: DataTypes.INTEGER,
allowNull: false,
references: {
model: 'NSMClients',
key: 'ClientId'
}
}
}, {
timestamps: false,
uniqueKeys: {
AliasUnique: {
fields: ['ClientId', 'OriginId']
}
}
})
NSMAliases.sync()
return NSMAliases
}

View file

@ -0,0 +1,35 @@
module.exports = (sequelize, DataTypes) => {
const NSMAudit = sequelize.define('NSMAudit',
{
Id: {
type: DataTypes.INTEGER,
autoIncrement: true,
allowNull: false,
primaryKey: true
},
Origin: {
type: DataTypes.INTEGER,
allowNull: false,
},
Type: {
type: DataTypes.TEXT,
allowNull: true,
defaultValue: null,
},
Description: {
type: DataTypes.TEXT,
allowNull: true,
defaultValue: null,
},
Date: {
type: DataTypes.DATE,
allowNull: false,
defaultValue: DataTypes.literal('CURRENT_TIMESTAMP'),
}
}, {
timestamps: false
})
NSMAudit.sync()
return NSMAudit
}

View file

@ -0,0 +1,51 @@
module.exports = (sequelize, DataTypes) => {
const NSMClients = sequelize.define('NSMClients',
{
ClientId: {
type: DataTypes.INTEGER,
autoIncrement: true,
allowNull: false,
primaryKey: true
},
Description: {
type: DataTypes.TEXT,
allowNull: true,
defaultValue: null,
},
Password: {
type: DataTypes.TEXT,
allowNull: true,
defaultValue: null,
},
Secret: {
type: DataTypes.TEXT,
allowNull: true,
defaultValue: null,
},
Guid: {
type: DataTypes.TEXT,
allowNull: false,
unique: true
},
PermissionLevel: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: 0,
},
FirstConnection: {
type: DataTypes.DATE,
allowNull: false,
defaultValue: DataTypes.literal('CURRENT_TIMESTAMP'),
},
LastConnection: {
type: DataTypes.DATE,
allowNull: false,
defaultValue: DataTypes.literal('CURRENT_TIMESTAMP'),
}
}, {
timestamps: false
})
NSMClients.sync()
return NSMClients
}

View file

@ -0,0 +1,41 @@
module.exports = (sequelize, DataTypes) => {
const NSMConnections = sequelize.define('NSMConnections',
{
Id: {
type: DataTypes.INTEGER,
autoIncrement: true,
allowNull: false,
primaryKey: true
},
ClientId: {
type: DataTypes.INTEGER,
allowNull: false,
references: {
model: 'NSMClients',
key: 'ClientId'
}
},
Name: {
type: DataTypes.TEXT,
allowNull: false,
},
Guid: {
type: DataTypes.TEXT,
allowNull: false,
},
IPAddress: {
type: DataTypes.TEXT,
allowNull: true,
},
Date: {
type: DataTypes.DATE,
allowNull: false,
defaultValue: DataTypes.literal('CURRENT_TIMESTAMP'),
}
}, {
timestamps: false
})
NSMConnections.sync()
return NSMConnections
}

View file

@ -0,0 +1,56 @@
module.exports = (sequelize, DataTypes) => {
const NSMKills = sequelize.define('NSMKills',
{
Id: {
type: DataTypes.INTEGER,
autoIncrement: true,
allowNull: false,
primaryKey: true
},
ClientId: {
type: DataTypes.INTEGER,
allowNull: false,
references: {
model: 'NSMClients',
key: 'ClientId'
}
},
TargetId: {
type: DataTypes.INTEGER,
allowNull: false,
references: {
model: 'NSMClients',
key: 'ClientId'
}
},
BaseWeapon: {
type: DataTypes.TEXT,
allowNull: true,
},
Weapon: {
type: DataTypes.TEXT,
allowNull: true,
},
MOD: {
type: DataTypes.TEXT,
allowNull: true,
},
Damage: {
type: DataTypes.INTEGER,
allowNull: true,
},
HitLoc: {
type: DataTypes.TEXT,
allowNull: true,
},
Date: {
type: DataTypes.DATE,
allowNull: false,
defaultValue: DataTypes.literal('CURRENT_TIMESTAMP'),
}
}, {
timestamps: false
})
NSMKills.sync()
return NSMKills
}

View file

@ -0,0 +1,40 @@
module.exports = (sequelize, DataTypes) => {
const NSMKills = sequelize.define('NSMMessages',
{
Id: {
type: DataTypes.INTEGER,
autoIncrement: true,
allowNull: false,
primaryKey: true
},
Name: {
type: DataTypes.TEXT,
allowNull: true,
},
OriginId: {
type: DataTypes.INTEGER,
allowNull: false,
references: {
model: 'NSMClients',
key: 'ClientId'
}
},
Message: {
type: DataTypes.TEXT,
allowNull: true,
},
Hostname: {
type: DataTypes.TEXT,
allowNull: true,
},
Date: {
type: DataTypes.DATE,
allowNull: false,
defaultValue: DataTypes.literal('CURRENT_TIMESTAMP'),
}
}, {
timestamps: false
})
NSMKills.sync()
return NSMKills
}

View file

@ -0,0 +1,37 @@
module.exports = (sequelize, DataTypes) => {
const NSMMeta = sequelize.define('NSMMeta',
{
Id: {
type: DataTypes.INTEGER,
autoIncrement: true,
allowNull: false,
primaryKey: true
},
ClientId: {
type: DataTypes.INTEGER,
allowNull: false,
references: {
model: 'NSMClients',
key: 'ClientId'
}
},
Key: {
type: DataTypes.TEXT,
allowNull: false,
},
Value: {
type: DataTypes.TEXT,
allowNull: false,
},
Date: {
type: DataTypes.DATE,
allowNull: false,
defaultValue: DataTypes.literal('CURRENT_TIMESTAMP'),
}
}, {
timestamps: false
})
NSMMeta.sync()
return NSMMeta
}

View file

@ -0,0 +1,53 @@
module.exports = (sequelize, DataTypes) => {
const NSMPenalties = sequelize.define('NSMPenalties',
{
Id: {
type: DataTypes.INTEGER,
autoIncrement: true,
allowNull: false,
primaryKey: true
},
TargetId: {
type: DataTypes.INTEGER,
allowNull: false,
references: {
model: 'NSMClients',
key: 'ClientId'
}
},
OriginId: {
type: DataTypes.INTEGER,
allowNull: false,
references: {
model: 'NSMClients',
key: 'ClientId'
}
},
PenaltyType: {
type: DataTypes.TEXT,
allowNull: false
},
Date: {
type: DataTypes.DATE,
allowNull: false,
defaultValue: DataTypes.literal('CURRENT_TIMESTAMP')
},
Active: {
type: DataTypes.BOOLEAN,
allowNull: false,
defaultValue: true
},
Duration: {
type: DataTypes.INTEGER,
allowNull: false
},
Reason: {
type: DataTypes.TEXT,
allowNull: false
}
}, {
timestamps: false
})
NSMPenalties.sync()
return NSMPenalties
}

View file

@ -0,0 +1,39 @@
module.exports = (sequelize, DataTypes) => {
const NSMPlayerStatHistory = sequelize.define('NSMPlayerStatHistory',
{
Id: {
type: DataTypes.INTEGER,
autoIncrement: true,
allowNull: false,
primaryKey: true
},
ClientId: {
type: DataTypes.INTEGER,
allowNull: false,
references: {
model: 'NSMClients',
key: 'ClientId'
}
},
TotalPerformance: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: 100,
},
Performance: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: 100,
},
Date: {
type: DataTypes.DATE,
allowNull: false,
defaultValue: DataTypes.literal('CURRENT_TIMESTAMP'),
}
}, {
timestamps: false,
freezeTableName: true
})
NSMPlayerStatHistory.sync()
return NSMPlayerStatHistory
}

View file

@ -0,0 +1,53 @@
module.exports = (sequelize, DataTypes) => {
const NSMPlayerStats = sequelize.define('NSMPlayerStats',
{
Id: {
type: DataTypes.INTEGER,
autoIncrement: true,
allowNull: false,
primaryKey: true
},
ClientId: {
type: DataTypes.INTEGER,
allowNull: false,
references: {
model: 'NSMClients',
key: 'ClientId'
}
},
PlayedTime: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: 0
},
Kills: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: 0,
},
Deaths: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: 0,
},
TotalPerformance: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: 100,
},
Performance: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: 100,
},
Event: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: 0,
}
}, {
timestamps: false
})
NSMPlayerStats.sync()
return NSMPlayerStats
}

View file

@ -0,0 +1,45 @@
module.exports = (sequelize, DataTypes) => {
const NSMReports = sequelize.define('NSMReports',
{
Id: {
type: DataTypes.INTEGER,
autoIncrement: true,
allowNull: false,
primaryKey: true
},
OriginId: {
type: DataTypes.INTEGER,
allowNull: false,
references: {
model: 'NSMClients',
key: 'ClientId'
}
},
TargetId: {
type: DataTypes.INTEGER,
allowNull: false,
references: {
model: 'NSMClients',
key: 'ClientId'
}
},
Reason: {
type: DataTypes.TEXT,
allowNull: false,
},
Active: {
type: DataTypes.BOOLEAN,
allowNull: false,
defaultValue: true
},
Date: {
type: DataTypes.DATE,
allowNull: false,
defaultValue: DataTypes.literal('CURRENT_TIMESTAMP'),
}
}, {
timestamps: false
})
NSMReports.sync()
return NSMReports
}

View file

@ -0,0 +1,35 @@
module.exports = (sequelize, DataTypes) => {
const NSMSettings = sequelize.define('NSMSettings',
{
ClientId: {
type: DataTypes.INTEGER,
autoIncrement: true,
allowNull: false,
primaryKey: true,
references: {
model: 'NSMClients',
key: 'ClientId'
}
},
TwoFactor: {
type: DataTypes.BOOLEAN,
allowNull: false,
defaultValue: false
},
InGameLogin: {
type: DataTypes.BOOLEAN,
allowNull: false,
defaultValue: false
},
TokenLogin: {
type: DataTypes.BOOLEAN,
allowNull: false,
defaultValue: true
}
}, {
timestamps: false
})
NSMSettings.sync()
return NSMSettings
}

View file

@ -0,0 +1,29 @@
module.exports = (sequelize, DataTypes) => {
const NSMTokens = sequelize.define('NSMTokens',
{
TokenId: {
type: DataTypes.INTEGER,
autoIncrement: true,
allowNull: false,
primaryKey: true
},
ClientId: {
type: DataTypes.INTEGER,
allowNull: false,
},
Token: {
type: DataTypes.TEXT,
allowNull: false,
},
Date: {
type: DataTypes.DATE,
allowNull: false,
defaultValue: DataTypes.literal('CURRENT_TIMESTAMP'),
}
}, {
timestamps: false
})
NSMTokens.sync()
return NSMTokens
}

View file

@ -0,0 +1,94 @@
const path = require('path')
const configuration = require(path.join(__dirname, `../Configuration/NLSConfiguration.json`).toString())
const ws = require('ws')
const fs = require('fs')
const https = require('https')
const http = require('http')
const spawn = require('child_process').spawn
const Tail = require('tail').Tail
class NodeLogServer {
constructor(config) {
this.logFile = config.logFile
this.bindPort = config.bindPort
try {
this.ssl = {
key: fs.readFileSync(config.ssl.key),
cert: fs.readFileSync(config.ssl.cert)
}
} catch (e) {
this.ssl = null
console.warn('Unable to load SSL certificate from configuration, starting server without SSL is not recommended, provide a valid certificate if possible')
}
this.key = config.key
this.init()
}
init() {
try {
const server = this.ssl ? https.createServer(this.ssl) : http.createServer()
const socket = new ws.Server({ server })
var getParams = (url) => {
var queryDict = {}
url.substr(1).split("&").forEach(function(item) {queryDict[item.split("=")[0]] = item.split("=")[1]})
return queryDict;
}
var filePath = path.resolve(this.logFile)
if (!fs.existsSync(filePath)) {
console.log(`Warning: log file "${filePath}" doesn't exist\nMake sure you selected the right file in Configuration/NLSConfiguration.json Servers -> LOGFILE\n`)
return
}
server.listen(this.bindPort, () => {
console.log(`Server listening on port ${this.bindPort}`)
})
socket.authorizedClients = []
socket.Broadcast = (msg) => {
socket.authorizedClients.forEach(client => {
client.send(msg)
})
}
socket.on('connection', (conn, req) => {
var params = getParams(req.url.substr(1))
if (params.key != this.key) {
console.log(`Rejecting connection from ${req.socket.remoteAddress}`)
conn.close()
return
}
console.log(`Accepting connection from ${req.socket.remoteAddress}`)
socket.authorizedClients.push(conn)
})
if (process.platform == 'win32') {
var tail = spawn(`powershell`, ['-command', 'get-content', '-wait', '-Tail 0', `"${filePath}"`])
tail.stdout.on('data', (data) => {
socket.Broadcast(data.toString())
})
return
}
var tail = new Tail(filePath)
tail.watch()
tail.on('line', async (data) => {
socket.Broadcast(data)
})
}
catch (e) {
console.log(`Log server failed to start: ${e.toString()}`)
}
}
}
configuration.Servers.forEach(config => {
new NodeLogServer(config)
})

View file

@ -0,0 +1,184 @@
process.on('uncaughtException', (err) => {
console.log('Caught exception: ' + err + err.stack)
})
const fs = require('fs')
const path = require('path')
const configured = fs.existsSync(path.join(__dirname, `../Configuration/NSMConfiguration.json`))
process.env.LOCALE = 'en'
const EventEmitter = require('events')
const ConfigMaker = require('./ConfigMaker.js')
var Info = {
Author: 'fed',
Version: require('child_process').execSync('git rev-parse HEAD').toString().trim().substr(0, 6)
}
var Managers = []
var Id = 0
class Logger {
constructor(dirName, fileName) {
this.fileName = fileName
this.dirName = dirName
}
writeLn(data) {
if (!fs.existsSync(this.dirName)) {
fs.mkdirSync(this.dirName)
}
data = `[Log] ${new Date()} - - ${data}\n`
fs.appendFile(path.join(this.dirName, this.fileName), data, (err) => {
if (err) console.log(err)
})
}
}
function COD2BashColor(string) {
return string.replace(new RegExp(/\^([0-9]|\:|\;)/g, 'g'), `\x1b[3$1m`)
}
console._log = (string) => {
console.log(`${COD2BashColor(string)}\x1b[0m`)
}
class NSM extends EventEmitter{
constructor (config) {
super()
this.config = config
this.Version = Info.Version
this.Author = Info.Author
this.IP = config.IP
this.PORT = config.PORT
this.PASSWORD = config.PASSWORD
this.LOGFILE = config.LOGFILE
this.LOGSERVERURI = config.LOGSERVERURI
this.logger = new Logger(path.join(__dirname, `../Log/`), `NSM-${this.IP}:${this.PORT}.log`)
this.Server = null
this.startAsync()
}
async startAsync() {
this.RconConnection = new RconConnection(this.IP, this.PORT, this.PASSWORD, this.config.Gamename)
this.Server = new Server(this.IP, this.PORT, this.RconConnection, Database, sessionStore, clientData, Managers, Id++, this, this.config)
this.eventLogWatcher = this.LOGFILE ? new EventLogWatcher(this.LOGFILE, this.Server, this) : new ServerLogWatcher(this.LOGSERVERURI, this.Server, this)
this.loadPlugins()
await this.Server.setDvarsAsync()
if (this.Server.Hostname) {
console.log(`Now watching \x1b[33m${COD2BashColor(this.Server.Hostname)}\x1b[0m at \x1b[35m${this.IP}:${this.PORT}\x1b[0m`)
} else {
console.log(`Not watching \x1b[35m${this.IP}:${this.PORT}\x1b[0m: communication failed`)
clearInterval(this.Server.HeartbeatInt)
return
}
await this.Server.loadClientsAsync()
this.eventLogWatcher.init()
this.emit('ready')
}
log(string) {
console.log(`[${new Date().toISOString()}] - - ${COD2BashColor(string)}`)
}
loadPlugins() {
const directoryPath = path.join(__dirname, '../Plugins');
fs.readdir(directoryPath, (err, files) => {
if (err) {
return console.log('Unable to scan directory: ' + err);
}
files.forEach( (file) => {
if (!file.match(/.+\.js/g)) return
this.logger.writeLn(`Loading plugin \x1b[33m${file}\x1b[0m for server ${this.Server.IP}:${this.Server.PORT}`)
try {
let plugin = require(path.join(__dirname, `../Plugins/${file}`))
new plugin(this.Server, this, Managers)
}
catch (e) {
console.log(`Error evaluating plugin \x1b[33m${file}\x1b[0m: \x1b[31m${e.toString()}\n${e.stack}\x1b[0m`)
}
})
})
}
}
if (configured) {
const configuration = require(path.join(__dirname, `../Configuration/NSMConfiguration.json`).toString())
process.env.LOCALE = configuration.locale ? fs.existsSync(path.join(__dirname, `../Configuration/Localization-${configuration.locale}.json`)) ? configuration.locale : 'en' : 'en'
var RconConnection = require('./RconConnection.js')
var Server = require(path.join(__dirname, '../Lib/Entity/Server.js'))
var Database = new (require(path.join(__dirname, '../Lib/InitDatabase.js')))()
var EventLogWatcher = require('./EventLogWatcher.js')
var ServerLogWatcher = require('./ServerLogWatcher.js')
var sessionStore = new (require(path.join(__dirname, `../Webfront/SessionStore.js`)))()
var clientData = new (require(path.join(__dirname, `../Lib/ClientData.js`)))()
process.env.config = JSON.stringify(require(path.join(__dirname, `../Configuration/NSMConfiguration.json`)))
process.env.Localization = JSON.stringify(require(path.join(__dirname, `../Configuration/Localization-${process.env.LOCALE}.json`)))
// var commitId = require('child_process').execSync('git rev-parse HEAD').toString().trim()
// var lastCommit = require('child_process').execSync('git ls-remote https://github.com/fedddddd/node-server-manager.git HEAD').toString().split(/\s+/g)[0].trim()
console.log(`+-------------------------------+`)
console.log(`| \x1b[31mZ-Tavern Fed Node\x1b[0m\t\t|`)
console.log(`| \x1b[33m Kiels \x1b[0m\t\t\t|`)
console.log(`| \x1b[35m Z-Tavern \x1b[0m\t\t\t|`)
console.log(`+-------------------------------+`)
/* console._log(commitId == lastCommit
? '^2Node Server Manager is up to date'
: `^3An update is available (v${commitId.substr(0, 6)}, run git pull to update)`)*/
console.log(`Environment: ${process.env.NODE_ENV == 'dev' ? 'Development' : 'Production'}`)
configuration.Servers.forEach(config => {
Managers.push(new NSM({ ...configuration, ...config }))
})
var masterServer = new (require('./MasterServer.js'))(Managers)
for (var i = 0; i < Managers.length; i++) {
Managers[i].Server.masterServer = masterServer
}
async function loadGlobalPlugins() {
const directoryPath = path.join(__dirname, '../Plugins/Global');
fs.readdir(directoryPath, (err, files) => {
if (err) {
return console.log('Unable to scan directory: ' + err);
}
files.forEach( (file) => {
if (!file.match(/.+\.js/g)) return
try {
let plugin = require(path.join(__dirname, `../Plugins/Global/${file}`))
new plugin(Managers)
}
catch (e) {
console.log(`Error evaluating plugin \x1b[33m${file}\x1b[0m: \x1b[31m${e.toString()}\x1b[0m`)
}
})
})
}
loadGlobalPlugins()
const _Webfront = require(path.join(__dirname, `../Webfront/Webfront.js`))
configuration.Webfront && (Webfront = new _Webfront(Managers, {
SSL: configuration.WebfrontSSL,
Key: configuration['WebfrontSSL-Key'],
Cert: configuration['WebfrontSSL-Cert'],
Port: configuration.WebfrontPort,
Hostname: configuration.WebfrontHostname,
}, sessionStore, Database))
new (require('./CLICommands.js'))(Managers[0], Managers)
masterServer.init()
} else {
var configMake = new ConfigMaker()
configMake.init()
}

View file

@ -0,0 +1,42 @@
module.exports = {
Rcon: {
prefix: '\xff\xff\xff\xffrcon %PASSWORD% %COMMAND%',
status: 'status',
getDvar: 'get %DVAR%',
setDvar: 'set %DVAR% %VALUE%',
clientKick: `clientkick %CLIENT% "%REASON%"`,
Tell: `tell %CLIENT% "%MESSAGE%"`,
Say: 'say "%MESSAGE%"',
statusRegex: /^ +([0-9]+) +([0-9]+) +([0-9]+){0,1} +([0-9]+) +((?:[A-Za-z0-9]){8,32}|(?:[A-Za-z0-9]){8,32}|bot[0-9]+|(?:[[A-Za-z0-9]+)) *(.{0,32}) +([0-9]+) +(\d+\.\d+\.\d+.\d+\:-*\d{1,5}|0+.0+:-*\d{1,5}|loopback|unknown|bot) +(-*[0-9]+) +([0-9]+) *$/g,
dvarRegex: /(.*?) +(is:|is) +\"(.*?)\"/g,
parseStatus: (match) => {
return {
num: match[1],
score: match[2],
bot: match[3],
ping: match[4],
guid: match[5],
name: match[6].replace(new RegExp(/\^([0-9]|\:|\;)/g, 'g'), ``),
lastmgs: match[7],
address: match[8],
qport: match[9],
rate: match[10]
}
}
},
convertGuid: (guid) => {
return guid
},
getInfo: '\xff\xff\xff\xffgetinfo',
getStatus: '\xff\xff\xff\xffgetstatus',
Dvars: {
maxclients: 'sv_maxclients',
mapname: 'mapname',
hostname: 'sv_hostname',
gamename: 'gamename',
maprotation: 'sv_mapRotation',
gametype: 'g_gametype',
messagelength: 999999999,
maxSayLength: 100
}
}

View file

@ -0,0 +1,40 @@
module.exports = {
Rcon: {
prefix: '\xff\xff\xff\xffrcon %PASSWORD% %COMMAND%',
status: 'status',
getDvar: '%DVAR%',
setDvar: '%DVAR% %VALUE%',
clientKick: `clientkick %CLIENT% "%REASON%"`,
Tell: `tell %CLIENT% "%MESSAGE%"`,
Say: `say "%MESSAGE%"`,
statusRegex: /^ +([0-9]+) +([0-9]+) +([0-9]+) +((?:[A-Za-z0-9]){8,32}|(?:[A-Za-z0-9]){8,32}|bot[0-9]+|(?:[[A-Za-z0-9]+)) +([0-9]+) *(.{0,32}) +([0-9]+) +(\d+\.\d+\.\d+.\d+\:-*\d{1,5}|0+.0+:-*\d{1,5}|loopback|unknown|bot) +(-*[0-9]+) +([0-9]+) *$/g,
dvarRegex: /\"(.*?)\" +(is:|is) +\"(.*?)\"/g,
parseStatus: (match) => {
return {
num: match[1],
score: match[2],
bot: '0',
ping: match[3],
guid: match[4],
steamid: match[5],
name: match[6].replace(new RegExp(/\^([0-9]|\:|\;)/g, 'g'), ``),
lastmgs: match[7],
address: match[8],
qport: match[9],
rate: match[10]
}
}
},
getInfo: '\xff\xff\xff\xffgetinfo',
getStatus: '\xff\xff\xff\xffgetstatus',
Dvars: {
maxclients: 'sv_maxClients',
mapname: 'mapname',
hostname: 'sv_hostname',
gametype: 'g_gametype',
gamename: 'gamename',
maprotation: 'sv_mapRotation',
messagelength: 999999999,
maxSayLength: 100
}
}

View file

@ -0,0 +1,39 @@
module.exports = {
Rcon: {
prefix: '\xff\xff\xff\xffrcon %PASSWORD% %COMMAND%',
status: 'status',
getDvar: '%DVAR%',
setDvar: '%DVAR% %VALUE%',
clientKick: `clientkick %CLIENT% "%REASON%"`,
Tell: `tellraw %CLIENT% "%MESSAGE%"`,
Say: `sayraw "%MESSAGE%"`,
statusRegex: /^ +([0-9]+) +([0-9]+) +([0-9]+) +([0-9]+) +((?:[A-Za-z0-9]){8,32}|(?:[A-Za-z0-9]){8,32}|bot[0-9]+|(?:[[A-Za-z0-9]+)) *(.{0,32}) +([0-9]+) +(\d+\.\d+\.\d+.\d+\:-*\d{1,5}|0+.0+:-*\d{1,5}|loopback|unknown|bot) +(-*[0-9]+) +([0-9]+) *$/g,
dvarRegex: /\"(.*?)\" +(is:|is) +\"(.*?)\"/g,
parseStatus: (match) => {
return {
num: match[1],
score: match[2],
bot: match[3],
ping: match[4],
guid: match[5],
name: match[6].replace(new RegExp(/\^([0-9]|\:|\;)/g, 'g'), ``),
lastmgs: match[7],
address: match[8],
qport: match[9],
rate: match[10]
}
}
},
getInfo: '\xff\xff\xff\xffgetinfo',
getStatus: '\xff\xff\xff\xffgetstatus',
Dvars: {
maxclients: 'sv_maxClients',
mapname: 'mapname',
hostname: 'sv_hostname',
gametype: 'g_gametype',
gamename: 'gamename',
maprotation: 'sv_mapRotation',
messagelength: 999999999,
maxSayLength: 100
}
}

View file

@ -0,0 +1,43 @@
module.exports = {
Rcon: {
prefix: '\xff\xff\xff\xffrcon %PASSWORD% %COMMAND%',
status: 'status',
getDvar: 'get %DVAR%',
setDvar: 'set %DVAR% %VALUE%',
clientKick: `clientkick %CLIENT% "%REASON%"`,
Tell: `tell %CLIENT% "%MESSAGE%"`,
Say: 'say "%MESSAGE%"',
statusRegex: /^ +([0-9]+) +([0-9]+) +([0-9]+) +([0-9]+) +((?:[A-Za-z0-9]){8,32}|(?:[A-Za-z0-9]){8,32}|bot[0-9]+|(?:[[A-Za-z0-9]+)) *(.{0,32}) +(\d+\.\d+\.\d+.\d+\:-*\d{1,5}|0+.0+:-*\d{1,5}|loopback|unknown|bot) +([0-9]+) *$/g,
dvarRegex: /(.*?) +(is:|is) +\"(.*?)\"/g,
parseStatus: (match) => {
const bot = match[3] == '1'
return {
num: match[1],
score: match[2],
bot,
ping: match[4],
guid: bot ? match[5] : parseInt(match[5].substr(8), 16).toString(),
name: match[6].replace(new RegExp(/\^([0-9]|\:|\;)/g, 'g'), ``),
address: bot ? 'localhost:27016' : match[7],
qport: match[8],
}
},
retries: 3
},
convertGuid: (guid) => {
return parseInt(guid.substr(8), 16).toString()
},
getInfo: '\xff\xff\xff\xffgetinfo',
getStatus: '\xff\xff\xff\xffgetstatus',
Dvars: {
maxclients: 'sv_maxclients',
mapname: 'mapname',
hostname: 'sv_hostname',
gamename: 'gamename',
maprotation: 'sv_mapRotation',
gametype: 'g_gametype',
messagelength: 999999999,
maxSayLength: 120
}
}

View file

@ -0,0 +1,39 @@
module.exports = {
Rcon: {
prefix: '\xff\xff\xff\xffrcon %PASSWORD% %COMMAND%',
status: 'status',
getDvar: '%DVAR%',
setDvar: '%DVAR% %VALUE%',
clientKick: `clientkick %CLIENT% "%REASON%"`,
Tell: `tellraw %CLIENT% "%MESSAGE%"`,
Say: `sayraw "%MESSAGE%"`,
statusRegex: /^ +([0-9]+) +([0-9]+) +(Yes|No) +([0-9]+) +((?:[A-Za-z0-9]){8,32}|(?:[A-Za-z0-9]){8,32}|bot[0-9]+|(?:[[A-Za-z0-9]+)) *(.{0,32}) +() +(\d+\.\d+\.\d+.\d+\:-*\d{1,5}|0+.0+:-*\d{1,5}|loopback|unknown|bot) +([0-9]+) *$/g,
dvarRegex: /\"(.*?)\" +(is:|is) +\"(.*?)\"/g,
parseStatus: (match) => {
return {
num: match[1],
score: match[2],
bot: match[3] == 'Yes',
ping: match[4],
guid: match[5],
name: match[6].replace(new RegExp(/\^([0-9]|\:|\;)/g, 'g'), ``),
lastmgs: match[7],
address: match[8],
qport: match[9],
rate: match[10]
}
}
},
getInfo: '\xff\xff\xff\xffgetinfo',
getStatus: '\xff\xff\xff\xffgetstatus',
Dvars: {
maxclients: 'sv_maxClients',
mapname: 'mapname',
hostname: 'sv_hostname',
gametype: 'g_gametype',
gamename: 'gamename',
maprotation: 'sv_mapRotation',
messagelength: 999999999,
maxSayLength: 100
}
}

View file

@ -0,0 +1,40 @@
module.exports = {
Rcon: {
prefix: '\xff\xff\xff\xffrcon %PASSWORD% %COMMAND%',
status: 'status',
getDvar: '%DVAR%',
setDvar: 'set %DVAR% %VALUE%',
clientKick: `clientkick %CLIENT% "%REASON%"`,
Tell: `tell %CLIENT% "%MESSAGE%"`,
Say: `say "%MESSAGE%"`,
statusRegex: /^ +([0-9]+) +([0-9]+) +([0-9]+) +([0-9]+) +(.{0,32}) +([0-9]+) +(\d+\.\d+\.\d+.\d+\:-*\d{1,5}|0+.0+:-*\d{1,5}|loopback|unknown|bot) +(-*[0-9]+) +([0-9]+) *$/g,
dvarRegex: /\"(.*?)\" +(is:|is) +\"(.*?)\"/g,
commandDelay: 500,
parseStatus: (match) => {
return {
num: match[1],
score: match[2],
bot: '0',
ping: match[3],
guid: match[4],
name: match[5].replace(new RegExp(/\^([0-9]|\:|\;)/g, 'g'), ``),
lastmgs: match[6],
address: match[7],
qport: match[8],
rate: match[9]
}
}
},
getInfo: '\xff\xff\xff\xffgetinfo',
getStatus: '\xff\xff\xff\xffgetstatus',
Dvars: {
maxclients: 'sv_maxClients',
mapname: 'mapname',
hostname: 'sv_hostname',
gametype: 'g_gametype',
gamename: 'gamename',
maprotation: 'sv_mapRotation',
messagelength: 999999999,
maxSayLength: 150
}
}

View file

@ -0,0 +1,39 @@
module.exports = {
Rcon: {
prefix: '\xff\xff\xff\xffrcon %PASSWORD% %COMMAND%',
status: 'status',
getDvar: 'get %DVAR%',
setDvar: 'set %DVAR% %VALUE%',
clientKick: `clientkick_for_reason %CLIENT% "%REASON%"`,
Tell: `tell %CLIENT% "%MESSAGE%"`,
Say: `say "%MESSAGE%"`,
statusRegex: /^ +([0-9]+) +([0-9]+) +([0-9]+) +([0-9]+) +((?:[A-Za-z0-9]){8,32}|(?:[A-Za-z0-9]){8,32}|bot[0-9]+|(?:[[A-Za-z0-9]+)) *(.{0,32}) +([0-9]+) +(\d+\.\d+\.\d+.\d+\:-*\d{1,5}|0+.0+:-*\d{1,5}|loopback|unknown|bot) +(-*[0-9]+) +([0-9]+) *$/g,
dvarRegex: /(.*?) +(is:|is) +\"(.*?)\"/g,
parseStatus: (match) => {
return {
num: match[1],
score: match[2],
bot: match[3],
ping: match[4],
guid: parseInt(match[5], 16).toString(),
name: match[6].replace(new RegExp(/\^([0-9]|\:|\;)/g, 'g'), ``),
lastmgs: match[7],
address: match[8],
qport: match[9],
rate: match[10]
}
}
},
getInfo: '\xff\xff\xff\xffgetinfo',
getStatus: '\xff\xff\xff\xffgetstatus',
Dvars: {
maxclients: 'sv_maxclients',
mapname: 'mapname',
hostname: 'sv_hostname',
gametype: 'g_gametype',
gamename: 'gamename',
maprotation: 'sv_mapRotation',
messagelength: 104,
maxSayLength: 100
}
}

View file

@ -0,0 +1,196 @@
const dgram = require('dgram')
const path = require('path')
const Utils = new (require(path.join(__dirname, '../Utils/Utils.js')))()
const Mutex = require(path.join(__dirname, '../Utils/Mutex.js'))
const fs = require('fs')
const wait = require('delay')
class Rcon {
constructor (ip, port, password, gamename) {
this.ip = ip
this.mutex = new Mutex()
this.port = port
this.password = password
this.gamename = gamename
this.commandPrefixes = fs.existsSync(path.join(__dirname, `./RconCommandPrefixes/${gamename}.js`))
? {...require(`./RconCommandPrefixes/Default.js`), ...require(`./RconCommandPrefixes/${gamename}.js`)}
: require(`./RconCommandPrefixes/Default.js`)
this.isRunning = false
this.commandRetries = 3
this.previousClients = []
this.canExecute = true
this.commandQueue = 0
this.client = dgram.createSocket('udp4')
}
async sendCommand(command) {
return new Promise(async (resolve, reject) => {
var client = dgram.createSocket('udp4')
var message = new Buffer.from(command, 'binary')
client.on('listening', async () => {
client.send(message, 0, message.length, this.port, this.ip, async (err) => {
if (err) {
client.close()
resolved = true
resolve(false)
}
})
})
client.bind()
var resolved = false;
var onMessage = (msg) => {
client.removeAllListeners()
client.close()
resolved = true
resolve(msg.toString())
}
client.on('message', onMessage);
setTimeout(() => {
if (!resolved) {
client.removeAllListeners()
client.close()
resolve(false)
}
}, 3000)
})
}
async executeCommandAsync(command) {
return new Promise(async (_resolve, reject) => {
if (this.commandPrefixes.Rcon.commandDelay) {
await this.mutex.lock()
}
const resolve = async (msg) => {
_resolve(msg)
if (this.commandPrefixes.Rcon.commandDelay) {
await wait(this.commandPrefixes.Rcon.commandDelay)
this.mutex.unlock()
}
}
const client = dgram.createSocket('udp4')
const message = new Buffer.from(Utils.formatString(this.commandPrefixes.Rcon.prefix, {
password: this.password,
command
})[0], 'binary')
const timeout = setTimeout(() => {
client.close()
client.removeAllListeners()
resolve(false)
}, 5000)
client.once('listening', async () => {
client.send(message, 0, message.length, this.port, this.ip, async (err) => {
if (err) {
clearTimeout(timeout)
client.close()
client.removeAllListeners()
resolve(false)
}
})
})
client.once('message', (data) => {
clearTimeout(timeout)
client.close()
resolve(data.toString())
})
client.bind()
})
}
async setDvar(dvar, value) {
const command = Utils.formatString(this.commandPrefixes.Rcon.setDvar, {
dvar,
value
})
await this.executeCommandAsync(command)
}
async getDvarRaw(dvarName) {
for (var i = 0; i < this.commandRetries; i++) {
var dvar = await this.executeCommandAsync(this.commandPrefixes.Rcon.getDvar.replace('%DVAR%', dvarName))
if (!dvar || !dvar.match(this.commandPrefixes.Rcon.dvarRegex)) continue
return this.commandPrefixes.Rcon.dvarRegex.exec(dvar)[3].trim()
}
return false
}
async getDvar(dvar) {
const command = Utils.formatString(this.commandPrefixes.Rcon.getDvar, {
dvar
})
for (var i = 0; i < this.commandRetries; i++) {
const string = await this.executeCommandAsync(command)
if (!string || !string.match(this.commandPrefixes.Rcon.dvarRegex)) {
continue
}
return Utils.stripString(this.commandPrefixes.Rcon.dvarRegex.exec(string)[3].trim())
}
return false
}
async getStatus() {
try {
var status = await this.executeCommandAsync(this.commandPrefixes.Rcon.status)
if (!status) return false
status = status.split('\n').slice(1, -1)
if (status[0].includes('invalid')) return false
var map = status[0].split(/\s+/g)[1]
var rawClients = status.slice(3)
var clients = []
rawClients.forEach(client => {
if (!client.match(this.commandPrefixes.Rcon.statusRegex)) return
var match = this.commandPrefixes.Rcon.statusRegex.exec(client)
for (var i = 0; i < match.length; i++) {
match[i] = match[i] ? match[i].trim() : ''
}
clients.push(this.commandPrefixes.Rcon.parseStatus(match))
})
}
catch (e) {
return false
}
return {success: true, data : {map, clients}}
}
async getClientByGuid(guid) {
var clients = (await this.getStatus()).data.clients
for (var i = 0; i < clients.length; i++) {
if (clients[i].guid == guid) {
return clients[i]
}
}
}
}
module.exports = Rcon

View file

@ -0,0 +1,56 @@
const EventParser = require('./EventParser.js')
const ws = require('ws')
const _EventDispatcher = require('./EventDispatcher.js')
class EventLogWatcher extends EventParser {
constructor (logServerURI, Server, Manager) {
super(Server)
this.logServerURI = logServerURI
this.Server = Server
this.Manager = Manager
this.EventDispatcher = new _EventDispatcher(Server, Manager)
}
async init () {
try {
var socket = new ws(this.logServerURI)
socket.onmessage = async (msg) => {
this.onLine(msg.data)
}
socket.on('error', (error) => {
console.log(`Server Log Watcher: ${error}`)
})
socket.onclose = async () => {
console.log(`Connection to log server (${this.logServerURI}) lost, reconnecting in 5 seconds...`)
setTimeout(() => {
this.init()
}, 5 * 1000)
}
}
catch (e) {
this.Manager.logger.writeLn(`Remote log server generated an error: ${e.toString()}`)
}
}
async onLine(line) {
line = line.replace(/[^\x20-\x7E]+/g, '')
this.Server.Rcon.isRunning = true
this.Server.emit('line', line)
this.Server.emit('stripped_line', line.trim().replace(new RegExp(/([0-9]+:[0-9]+)\s+/g), ''))
const lines = line.split('\n').filter(l => l.length > 0)
for (var i = 0; i < lines.length; i++) {
const event = this.parseEvent(lines[i].trim())
if (!event) return
this.EventDispatcher.dispatchCallback(event)
}
}
}
module.exports = EventLogWatcher

Binary file not shown.

View file

@ -0,0 +1,6 @@
{
"ExpandedNodes": [
""
],
"PreviewInSolutionExplorer": false
}

Binary file not shown.

View file

@ -0,0 +1,205 @@
const path = require('path')
const Localization = JSON.parse(process.env.Localization).lookup
const fs = require('fs')
const fetch = require('node-fetch')
const { Command } = require(path.join(__dirname, `../Lib/Classes.js`))
const ipRangeCheck = require('ip-range-check')
const Utils = new (require(path.join(__dirname, '../Utils/Utils.js')))()
const wait = require('delay')
class Plugin {
constructor(Server, Manager) {
this.Server = Server
this.Manager = Manager
this.Server.on('connect', this.onPlayerConnected.bind(this))
this.Server.on('preconnect', this.onPlayerConnected.bind(this))
this.configPath = path.join(__dirname, '../Configuration/AntiVPNConfiguration.json')
this.config = {
blacklist: [],
whitelist: [],
clients: []
}
this.init()
}
async saveConfig() {
return new Promise((resolve, reject) => {
fs.writeFile(this.configPath, JSON.stringify(this.config, null, 4), async (err) => {
resolve()
})
})
}
async init() {
if (!fs.existsSync(this.configPath)) {
fs.writeFileSync(this.configPath, JSON.stringify(this.config, null, 4))
}
this.config = require(this.configPath)
var commands = new Command()
.setName('antivpn')
.setAlias('avpn')
.setPermission('ROLE_ADMIN')
.addParam({
name: 'action'
})
.addCallback(async (Player, params, args) => {
switch (params.action.toLocaleLowerCase()) {
case 'reset':
this.config = {
blacklist: [],
whitelist: [],
clients: []
}
this.saveConfig()
Player.Tell(Localization['AVPN_RESET'])
break
case 'clients':
switch (true) {
case (args.length == 2):
Player.Tell(Utils.va(Localization['AVPN_LIST'],
params.action.toLocaleLowerCase(),
this.config[params.action.toLocaleLowerCase()].length
))
return
case (args[2].toLocaleLowerCase() == 'flush'):
this.config.clients = []
Player.Tell(Utils.va(Localization['AVPN_FLUSH'], params.action))
this.saveConfig()
return
case (args.length < 4):
Player.Tell(Localization['COMMAND_ARGUMENT_ERROR'])
return
}
var Client = await this.Server.getClient(args[3])
if (!Client) {
Player.Tell(Localization['COMMAND_CLIENT_NOT_FOUND'])
return
}
switch (args[2].toLocaleLowerCase()) {
case 'add':
var found = false
for (var i = 0; i < this.config.clients.length; i++) {
if (this.config.clients[i] == Client.ClientId) {
found = true
}
}
!found && this.config.clients.push(Client.ClientId)
Player.Tell(Utils.va(Localization['AVPN_ADD_CLIENT'], Client.ClientId))
break
case 'remove':
for (var i = 0; i < this.config.clients.length; i++) {
if (this.config.clients[i] == Client.ClientId) {
this.config.clients.splice(i, 1)
}
}
Player.Tell(Utils.va(Localization['AVPN_REMOVE_CLIENT'], Client.ClientId))
break
default:
Player.Tell(Utils.va(Localization['COMMAND_ARGUMENT_INVALID'], args[2], '[add, remove]'))
return
}
this.saveConfig()
break
case 'blacklist':
case 'whitelist':
if (args.length == 2) {
Player.Tell(Utils.va(Localization['AVPN_LIST'], params.action.toLocaleLowerCase(), this.config[params.action.toLocaleLowerCase()].length))
return
}
switch (args[2].toLocaleLowerCase()) {
case 'flush':
this.config[params.action.toLocaleLowerCase()] = []
Player.Tell(Utils.va(Localization['AVPN_FLUSH'], params.action))
break
case 'add':
if (args.length < 4) {
Player.Tell(Localization['COMMAND_ARGUMENT_ERROR'])
return
}
this.config[params.action.toLocaleLowerCase()].push(args[3])
Player.Tell(Utils.va(Localization['AVPN_ADD_ADDRESS'], args[3]))
break
case 'remove':
if (args.length < 4) {
Player.Tell(Localization['COMMAND_ARGUMENT_ERROR'])
return
}
for (var i = 0; i < this.config[params.action.toLocaleLowerCase()].length; i++) {
if (this.config[params.action.toLocaleLowerCase()][i] == args[3]) {
this.config[params.action.toLocaleLowerCase()].splice(i, 1);
}
}
Player.Tell(Utils.va(Localization['AVPN_REMOVE_ADDRESS'], args[3]))
break
default:
Player.Tell(Utils.va(Localization['COMMAND_ARGUMENT_INVALID'], args[2], '[add, remove]'))
return
}
this.saveConfig()
break
case 'help':
var help = Localization['AVPN_HELP'].split('\n')
for (var i = 0; i < help.length; i++) {
Player.Tell(help[i])
await wait(300)
}
break
default:
Player.Tell(Utils.va(Localization['COMMAND_ARGUMENT_INVALID'], params.action, '[whitelist, blacklist, clients, help]'))
return
}
})
this.Manager.Commands.add(commands)
}
async onPlayerConnected(Player) {
try {
if (!Player.IPAddress || this.config.clients.indexOf(Player.ClientId) != -1) {
return
}
var address = Player.IPAddress.split(':')[0]
for (var i = 0; i < this.config.blacklist.length; i++) {
if (ipRangeCheck(address, this.config.blacklist[i])) {
Player.Kick(Localization['AVPN_BLACKLISTED'])
return
}
}
for (var i = 0; i < this.config.whitelist.length; i++) {
if (ipRangeCheck(address, this.config.whitelist[i])) {
return
}
}
var result = (await (await fetch(`https://api.xdefcon.com/proxy/check/?ip=${address}`)).json())
if (result.proxy) {
Player.Kick(Localization['PENALTY_VPN_KICK'])
}
}
catch (e) {}
}
}
module.exports = Plugin

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,98 @@
const path = require('path')
const config = require(path.join(__dirname, `../Configuration/NSMConfiguration.json`))
const { Webhook, MessageBuilder } = require('discord-webhook-node')
const hook = new Webhook({ url: config.discordHookUrl, throwErrors: false, retryOnLimit: false,})
const fetch = require('node-fetch')
const Utils = new (require(path.join(__dirname, '../Utils/Utils.js')))()
const https = require('https')
hook.setUsername('NSM Bot')
class Plugin {
constructor(Server, Manager) {
this.Server = Server
this.Manager = Manager
this.Url = null
this.Server.on('connect', this.onPlayerConnect.bind(this))
this.Server.on('disconnect', this.onPlayerDisconnect.bind(this))
this.Server.on('penalty', this.onPlayerPenalty.bind(this))
}
async onPlayerConnect (Player) {
this.sendHook(`:inbox_tray: ${Player.Name}`, ' ' ,`${await this.getUrl()}/id/${Player.ClientId}`)
Player.on('message', async (Message) => {
this.sendHook(`:envelope_with_arrow: ${Player.Name}`, Message, `${await this.getUrl()}/id/${Player.ClientId}`)
})
}
async onPlayerDisconnect (Player) {
this.sendHook(`:outbox_tray: ${Player.Name}`, ' ' ,`${await this.getUrl()}/id/${Player.ClientId}`)
}
async getUrl() {
if (this.Url) return this.Url
try {
var result = (await fetch(`${config.WebfrontSSL ? 'https://' : 'http://'}${config.webfrontHostname}/api/verify`))
var hostname = result ? config.webfrontHostname : `${(await fetch('https://api.ipify.org/?format=json')).json().ip}:${config.WebfrontPort}`
this.Url = `${config.WebfrontSSL ? 'https://' : 'http://'}${hostname}`
this.Url = this.Url
}
catch (e) {
try {
var hostname = (await (await fetch('https://api.ipify.org/?format=json')).json()).ip
this.Url = `${config.WebfrontSSL ? 'https://' : 'http://'}${hostname}:${config.WebfrontPort}`
}
catch (e) {
return null
}
}
return this.Url
}
async getFlag (IPAddress) {
return (await (await fetch(`https://extreme-ip-lookup.com/json/${IPAddress.split(':')[0]}?key=demo`)).json()).countryCode.toLocaleLowerCase()
}
async onPlayerPenalty(Type, Target, Reason, Origin, Duration = -1) {
var translation = {
'PENALTY_TEMP_BAN': 'Temp ban',
'PENALTY_PERMA_BAN': 'Perma ban',
'PENALTY_KICK': 'Kick',
'PENALTY_MUTE': 'Mute'
}
this.sendHookPenalty(`:hammer: ${Target.Name}`, ' ', `${await this.getUrl()}/id/${Target.ClientId}`, translation[Type], Reason, Origin, Duration)
}
async sendHookPenalty(Title, Description, Url, Type, Reason, Origin, Duration) {
var messageEmbed = new MessageBuilder()
.setTitle(Title)
.setDescription(Description)
.setURL(Url)
.setColor('#00b0f4')
.addField('Type', Type, true)
.addField('Origin', Origin.Name, true)
.addField('Reason', `\`${this.stripColorCodes(Reason)}\``, true)
.setFooter('Node Server Manager')
.setTimestamp()
Duration > 0 && messageEmbed.addField('Duration', Utils.time2str(Duration), true)
hook.send(messageEmbed)
}
stripColorCodes(string) {
return string.replace(new RegExp(/\^([0-9]|\:|\;)/g, 'g'), '')
}
async sendHook(Title, Description, Url) {
try {
var messageEmbed = new MessageBuilder()
.setTitle(Title)
.setDescription(Description)
.setURL(Url)
.setColor('#00b0f4')
.addField('Hostname', `\`${this.Server.HostnameRaw.replace(new RegExp(/\^([0-9]|\:|\;)/g, 'g'), '')}\``, true)
.addField('Map', `\`${this.Server.Mapname}\``, true)
.addField('Players', `\`${this.Server.Clients.filter((value) => {return value}).length} / ${this.Server.MaxClients}\``, true)
.setFooter('Node Server Manager')
.setTimestamp();
hook.send(messageEmbed)
}
catch (e) {}
}
}
module.exports = Plugin

View file

@ -0,0 +1,84 @@
const path = require('path')
const fs = require('fs')
const Utils = new (require(path.join(__dirname, '../../Utils/Utils.js')))()
const configName = path.join(__dirname, `../../Configuration/NSMConfiguration.json`)
var config = require(configName)
fs.watch(path.join(__dirname, `../../Configuration/NSMConfiguration.json`), async (filename) => {
if (filename) {
try { var newData = require(configName) }
catch (e) {
console.log(`Failed to reload config file ${configName}: ${e.toString()}`); return }
config = newData
console.log(`Reloaded config file ${configName}`)
}
})
class Plugin {
constructor(Managers) {
this.Managers = Managers
this.autoMessages()
}
autoMessages() {
setInterval(async () => {
var index = Utils.getRandomInt(0, config.autoMessages.length)
var Message = await this.replacePlaceholders(config.autoMessages[index])
this.Managers.forEach(Manager => {
Manager.Server.Broadcast(Message)
})
}, config.autoMessagesInterval * 1000)
}
async replacePlaceholders(text) {
var placeholders = {
'TOTALCLIENTS' : {
async get() {
return (await placeholders.Managers[0].Server.DB.getAllClients())
}
},
'PLAYERCOUNT': {
async get() {
var count = 0;
var Managers = placeholders.Managers.concat()
Managers.forEach(Manager => {
count += Manager.Server.Clients.filter((x) => { return x }).length
})
return count
}
},
'SERVERCOUNT': {
async get() {
var Managers = placeholders.Managers.concat()
return Managers.filter((Manager) => { return Manager.Server.Mapname} ).length
}
},
'TOTALKILLS': {
async get() {
return (await placeholders.Managers[0].Server.DB.getGlobalStats()).totalKills
}
},
'TOTALPLAYEDTIME': {
async get() {
return parseInt(((await placeholders.Managers[0].Server.DB.getGlobalStats()).totalPlayedTime) / 60)
}
}
}
placeholders.Managers = this.Managers
var entries = Object.entries(placeholders)
text = text.split(/\s+/g)
for (var i = 0; i < text.length; i++) {
for (var o = 0; o < entries.length; o++) {
if (text[i].includes(`{${entries[o][0]}}`)) {
text[i] = text[i].replace(`{${entries[o][0]}}`, (await entries[o][1].get()))
}
}
}
return text.join(' ')
}
}
module.exports = Plugin

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,53 @@
const path = require('path')
const Localization = require(path.join(__dirname, `../../Configuration/Localization-${process.env.LOCALE}.json`)).lookup
const Utils = new (require(path.join(__dirname, '../../Utils/Utils.js')))()
class Plugin {
constructor(Managers) {
this.Managers = Managers
this.init()
}
init() {
this.Managers.forEach(Manager => {
Manager.Server.on('message', this.playerMessage.bind(this))
})
}
playerMessage(Player, Message) {
if (Player.Session && Player.Session.Data.serverChat) {
Player.Session.Data.serverChat.Broadcast(Utils.formatString(Localization['GLOBALCHAT_FORMAT'], {
Enabled: '',
Name: Player.Name,
Message,
Hostname: Player.Server.Hostname
}))
}
this.Managers.forEach(async Manager => {
Manager.Server.Clients.forEach(Client => {
if (!Client || !Client.Session) return
if (Client.Session.Data.serverChat
&& Client.Session.Data.serverChat.Id == Player.Server.Id
&& (!Player.Session.Data.serverChat || Player.Session.Data.serverChat && Player.Session.Data.serverChat.Id != Client.Server.Id)) {
Client.Tell(Utils.formatString(Localization['SOCKET_MSG_FORMAT'], {
Name: Player.Name,
Message,
}, '%')[0])
return
}
if (!Client.Session.Data.globalChat
|| Client.Server.Id == Player.Server.Id) return
Client.Tell(Utils.formatString(Localization['GLOBALCHAT_FORMAT'], {
Enabled: (Player.Session && Player.Session.Data.globalChat) ? '[^1G^7]' : '',
Name: Player.Name,
Message,
Hostname: Player.Server.HostnameRaw
}, '%')[0])
})
})
}
}
module.exports = Plugin

View file

@ -0,0 +1,31 @@
class Plugin {
constructor(Managers) {
this.DB = Managers[0].Server.DB
this.addClientMeta()
}
async getZStats(ClientId) {
var Stats = (await this.DB.Models.NSMZStats.findAll({where: ClientId})).map(x => x = x.dataValues)
return Stats.length > 0 ? Stats[0] : false
}
async addClientMeta() {
this.DB.clientProfileMeta.push(async (ClientId) => {
var stats = await this.getZStats(ClientId)
if (!stats || stats.Score <= 500) return {}
return {
name: 'Zombies Stats',
data: {
'Kills': stats.Kills,
'Downs': stats.Downs,
'Revives': stats.Revives,
'Highest Round': stats.HighestRound,
'Headshots': stats.Headshots,
'Score': stats.Score,
}
}
})
}
}
module.exports = Plugin

View file

@ -0,0 +1,416 @@
const path = require('path')
const { Command } = require(path.join(__dirname, `../Lib/Classes.js`))
const Localization = require(path.join(__dirname, `../Configuration/Localization-${process.env.LOCALE}.json`)).lookup
const Games = require(path.join(__dirname, `../Configuration/Localization-${process.env.LOCALE}.json`)).Games
const config = require(path.join(__dirname, `../Configuration/NSMConfiguration.json`))
const Utils = new (require(path.join(__dirname, '../Utils/Utils.js')))()
const mathjs = require('mathjs')
const wait = require('delay')
class Plugin {
constructor(Server, Manager, Managers) {
this.Server = Server
this.Manager = Manager
this.Managers = Managers
this.init()
}
init() {
(() => {
let command = new Command()
.setName('calculator')
.setAlias('calc')
.addParam({
index: 0,
name: 'expression',
join: true
})
.addCallback(async (Player, params, args, options, funcs) => {
try {
var result = mathjs.evaluate(params.expression)
result ? funcs.Tell( Utils.formatString(Localization['COMMAND_CALC_RESULT'], { result: result.toString() }, '%')[0] ) : funcs.Tell(Utils.formatString(Localization['COMMAND_CALC_RESULT'], { result: Localization['COMMAND_CALC_FAIL'] }, '%')[0])
}
catch (e) {
funcs.Tell(Utils.formatString(Localization['COMMAND_CALC_RESULT'], { result: Localization['COMMAND_CALC_FAIL'] }, '%')[0])
}
})
this.Manager.Commands.add(command)
})(this);
(() => {
let command = new Command()
.setName('toggle')
.setAlias('t')
.addParam({
index: 0,
name: 'setting'
})
.addCallback(async (Player, params, args, options, funcs) => {
let settingsMeta = ['location']
if (!settingsMeta.includes(params.setting.toLocaleLowerCase())) {
Player.Tell(Localization['SETTING_NOT_EXIST'])
return
}
var setting = await this.Server.DB.metaService.getPersistentMeta(params.setting, Player.ClientId)
this.Server.DB.metaService.addPersistentMeta(params.setting.toLocaleLowerCase(), !(setting && setting.Value == '1'), Player.ClientId)
switch (params.setting.toLocaleLowerCase()) {
case 'location':
Player.Tell(Utils.formatString(Localization['SETTING_TOGGLE_FORMAT'], {setting: Utils.capitalizeFirstLetter(params.setting), value: !(setting && setting.Value == '1') ? '^1hidden' : '^2shown'}, '%')[0])
break
}
})
this.Manager.Commands.add(command)
})(this);
(() => {
let command = new Command({
isMiddleware: true
})
.addCallback(async (Player, params, args, options, funcs, next) => {
if (!config.socialMedia) {
next()
return
}
var sc = config.socialMedia.find((a) => a[0].toLocaleLowerCase() == args[0].toLocaleLowerCase())
if (!sc) {
next()
return
}
funcs.Tell(Utils.formatString(Localization['COMMAND_LINKS_FORMAT'], {name: sc[0], url: sc[1]}, '%')[0])
})
this.Manager.Commands.add(command)
})(this);
(() => {
let command = new Command({
name: 'reports',
alias: 'reps',
permission: 'ROLE_MODERATOR'
})
.addParam({
index: 0,
name: 'page',
optional: true
})
.setPermission('ROLE_MODERATOR')
.addCallback(async (Player, params, args, options, funcs) => {
if (params.page == 'clear') {
this.Server.DB.clearReports()
Player.Tell(Localization['COMMAND_REPORTS_CLEAR'])
return
}
var Reports = Utils.chunkArray(await this.Server.DB.getActiveReports(), Player.inGame ? 4 : 15)
if (!Reports.length) {
Player.Tell(Localization['COMMAND_NO_RESULT'])
return
}
var page = params.page ? Math.max(1, Math.min(parseInt(params.page), Reports.length)) : 1
await Player.Tell(Utils.formatString(Localization['COMMAND_LIST_PAGE'], {max: Reports.length, current: page}, '%')[0])
Player.inGame && await wait(300)
for (var i = 0; i < Reports[page - 1].length; i++) {
var TargetName = await this.Server.DB.getName(Reports[page - 1][i].TargetId)
var OriginName = await this.Server.DB.getName(Reports[page - 1][i].OriginId)
Player.Tell(Utils.formatString(Localization['COMMAND_REPORTS_TELL'], {Origin: OriginName, Target: TargetName, Reason: Reports[page - 1][i].Reason}, '%')[0])
Player.inGame && await wait(300)
}
})
this.Manager.Commands.add(command)
})(this);
(() => {
let command = new Command()
.setName('report')
.setAlias('rep')
.setInGame(true)
/* .addParams([
{
index: 0,
name: 'target'
},
{
index: 1,
name: 'reason',
join: true
}
])*/
.addException(Utils.formatString(Localization['COMMAND_REPORT_COOLDOWN'], {time: 5}, '%')[0], (Player) => {
return !Player.Data.lastReport || (new Date() - Player.Data.lastReport) / 1000 > 300
})
.addCallback( async (Player, params, args, options, funcs) => {
Player.Tell("To ^1report^7, take ^3video/photo evidences^7 & create a ^6Discord ^2Ticket^7.")
return
var Client = await this.Server.getClient(params.target)
if (!Client) {
Player.Tell(Localization['COMMAND_CLIENT_NOT_FOUND'])
return
}
this.Server.DB.addReport(Player.ClientId, Client.ClientId, params.reason)
Player.Data.lastReport = new Date()
Player.Tell(Localization['COMMAND_REPORT_SUCCESS'])
this.Server.emit('report', Player, Client, params.reason)
this.Server.tellStaffGlobal(Utils.formatString(Localization['COMMAND_REPORT_TELL'], {Origin: Player.Name, Hostname: Player.Server.HostnameRaw,Target: Client.Name, Reason: params.reason}, '%')[0])
})
this.Manager.Commands.add(command)
})(this);
(() => {
let command = new Command()
.setName('staff')
.addCallback(async (Player, params, args, options, funcs) => {
var staff = []
this.Managers.forEach(Manager => {
staff = staff.concat(Manager.Server.getStaffMembers())
})
if (!staff.length) {
funcs.Tell(Localization['COMMAND_STAFF_NO_RESULT'])
return
}
for (var i = 0; i < staff.length; i++) {
funcs.Tell(Utils.formatString(Localization['COMMAND_STAFF_FORMAT'], {
Name: staff[i].Name,
Level: staff[i].PermissionLevel,
Role: Utils.getRoleFrom(staff[i].PermissionLevel, 1).Name,
ClientId: staff[i].ClientId,
Hostname: staff[i].Server.HostnameRaw
}, '%')[0])
Player.inGame && await wait(500)
}
})
this.Manager.Commands.add(command)
})(this);
(() => {
let command = new Command()
.setName('eval')
.addParam({
index: 0,
name: 'js',
join: true
})
.setPermission('ROLE_OWNER')
.addCallback(async (Player, params, args, options, funcs) => {
try {
console.log(params.js)
eval(params.js)
}
catch (e) {
Player.Tell(e.toString())
}
})
if (process.env.NODE_ENV == 'dev')
this.Manager.Commands.add(command)
})(this);
(() => {
let command = new Command()
.setName('stats')
.addParam({
index: 0,
name: 'client',
join: true,
optional: true
})
.addCallback(async (Player, params, args, options, funcs) => {
var Target = !params.client ? Player : await this.Server.getClient(params.client)
if (!Target) {
Player.Tell(Localization.COMMAND_CLIENT_NOT_FOUND)
return
}
var ClientId = Target.ClientId
var Stats = await this.Server.DB.getPlayerStatsTotal(ClientId)
var Client = await this.Server.DB.getClient(ClientId)
if (Stats)
funcs.Tell(Localization.COMMAND_STATS_FORMAT
.replace('%PLAYEDTIME%', Utils.time2str(Stats.PlayedTime * 60))
.replace('%PERFORMANCE%', Stats.Performance.toFixed(2))
.replace('%NAME%', Client.Name)
.replace('%KILLS%', Stats.Kills)
.replace('%DEATHS%', Stats.Deaths)
.replace('%KDR%',(Stats.Kills / Math.max(Stats.Deaths, 1)).toFixed(2)))
else funcs.Tell(Localization.COMMAND_CLIENT_NOT_FOUND)
})
this.Manager.Commands.add(command)
})(this);
(() => {
let command = new Command()
.setName('uptime')
.setAlias('ut')
.setInGame(true)
.addCallback(async (Player, params, args, options, funcs) => {
funcs.Tell(Utils.formatString(Localization['COMMAND_UPTIME_FORMAT'], {uptime: Utils.time2str(this.Server.uptime)}, '%')[0])
})
this.Manager.Commands.add(command)
})(this);
(() => {
let command = new Command()
.setName('status')
.addCallback(async (Player, params, args, options, funcs) => {
funcs.Tell(Utils.formatString(Localization['COMMAND_SUMMARY_FORMAT'], {
totalClients: await this.Server.DB.getAllClients(),
totalServers: this.Managers.filter(m => m.Server.Rcon.isRunning).length,
clientsToday: (await this.Server.DB.getLastConnections()).length,
uniqueToday: (await this.Server.DB.getLastUniques()).length,
onlineClients: this.Managers.reduce((a, {Server}) => a + Server.getClients().length, 0),
totalSlots: this.Managers.reduce((a, {Server}) => a + Server.Clients.length, 0)
}, '%')[0])
})
this.Manager.Commands.add(command)
})(this);
(() => {
let command = new Command()
.setName('rotation')
.setAlias('rr')
.setInGame(true)
.addCallback(async (Player, params, args, options, funcs) => {
var buffer = ""
this.Server.mapRotation.forEach((map, i) => {
buffer += Utils.va("%s%s%s",
map == this.Server.Mapname ? '^3' : '^5',
this.Server.getMap(map) ? this.Server.getMap(map).Alias : map,
i < this.Server.mapRotation.length - 1 ? '^7, ' : ''
)
})
funcs.Tell(buffer)
})
this.Manager.Commands.add(command)
})(this);
(() => {
let command = new Command()
.setName('chat')
.setInGame(true)
.addParam({
name: 'server',
optional: true,
join: true
})
.addCallback(async (Player, params) => {
if (!params.server) {
if (Player.Session.Data.serverChat) {
Player.Session.Data.serverChat.Broadcast(Utils.formatString(Localization['SERVERCHAT_DISCONNECTED'], {
Name: Player.Name
}, '%')[0])
}
Player.Session.Data.serverChat = undefined
Player.Tell(Localization['SERVERCHAT_DISABLED'])
return
}
var Manager = this.Managers.find(Manager => Utils.cleanIncludes(Manager.Server.Hostname, params.server))
if (!Manager) {
Player.Tell(Localization['SERVER_NOT_FOUND'])
return
}
if (Player.Session.Data.serverChat && Player.Session.Data.serverChat.Id != Manager.Server.Id) {
Player.Session.Data.serverChat.Broadcast(Utils.formatString(Localization['SERVERCHAT_DISCONNECTED'], {
Name: Player.Name
}, '%')[0])
}
Player.Session.Data.serverChat = Manager.Server
Manager.Server.Broadcast(Utils.formatString(Localization['SERVERCHAT_CONNECTED'], {
Name: Player.Name
}, '%')[0])
Player.Tell(Utils.formatString(Localization['SERVERCHAT_ENABLED'], {
Hostname: Manager.Server.Hostname
}, '%')[0])
})
this.Manager.Commands.add(command)
})(this);
(() => {
let command = new Command()
.setName('rules')
.addParam({
name: 'page',
optional: true
})
.addCallback(async (Player, params) => {
if (!this.Server.config.rules) {
Player.Tell(Localization['COMMAND_RULES_UNDEFINED'])
return
}
const size = Player.inGame ? 4 : 15
const chunkedRules = Utils.chunkArray(this.Server.config.rules, size)
const page = Math.max(0, Math.min(params.page ? parseInt(params.page) - 1 : 0, chunkedRules.length))
const rules = chunkedRules[page]
Player.Tell(Utils.formatString(Localization['COMMAND_LIST_PAGE'], {
current: page + 1,
max: chunkedRules.length
}, '%')[0])
Player.inGame && await wait(300)
for (var i = 0; i < rules.length; i++) {
Player.Tell(Utils.va(Localization['COMMAND_RULES_FORMAT'], size * page + i + 1, rules[i]))
Player.inGame && await wait(500)
}
})
this.Manager.Commands.add(command)
})(this);
(() => {
let command = new Command()
.setName('resetstats')
.setAlias('rs')
.addCallback(async (Player, params, args, options, funcs) => {
await this.Server.DB.resetStats(Player.ClientId)
funcs.Tell(Localization['COMMAND_RESETSTATS_RESET'])
})
this.Manager.Commands.add(command)
})(this);
}
}
module.exports = Plugin

View file

@ -0,0 +1,800 @@
const moment = require('moment')
const path = require('path')
const crypto = require('crypto')
const wait = require('delay')
const fs = require('fs')
const Permissions = require(path.join(__dirname, `../Configuration/NSMConfiguration.json`)).Permissions
const configName = path.join(__dirname, `../Configuration/NSMConfiguration.json`)
const Localization = require(path.join(__dirname, `../Configuration/Localization-${process.env.LOCALE}.json`)).lookup
const Utils = new (require(path.join(__dirname, '../Utils/Utils.js')))()
var config = require(configName)
fs.watch(configName, async (filename) => {
if (filename) {
try { var newData = require(configName) }
catch (e) {
console.log(`Failed to reload config file ${configName}: ${e.toString()}`); return }
config = newData
}
})
class Plugin {
constructor(Server, Manager, Managers) {
this.Server = Server
this.Manager = Manager
this.Managers = Managers
//add the .pguid of players to grant them staff permissions (must be done on ClanTag, ZombiesBank, ZombiesStats, NativeCommands & the gsc script staff.gsc)
this.staff_list_a = [564391]
this.init()
}
onEventAsync (event) {
switch (event.type) {
case 'say':
if (config.commandPrefixes.includes(event.data.Message[0]) || config.broadcastCommandPrefixes.includes(event.data.Message[0]))
this.playerCommand(event.data.Origin, event.data.Message.substr(1).split(/\s+/), event.data.Message[0])
break
}
}
init () {
this.Manager.commands = {
'help': {
ArgumentLength: 0,
Permission: Permissions.Commands.COMMAND_USER_CMDS,
inGame: false,
callback: async (Player, args = null, delay) => {
var commands = Object.entries({...this.Manager.commands, ...this.Manager.Commands.Commands})
.filter(command => {
return !command[1].isMiddleware && (Permissions.Levels[command[1].Permission] <= Player.PermissionLevel || command[1].PermissionLevel <= Player.PermissionLevel)
})
switch (true) {
case (!args[1]):
case (Number.isInteger(parseInt(args[1]))):
var chunkedCommands = Utils.chunkArray(commands, Player.inGame ? 4 : 15)
var page = args[1] ? Math.max(1, Math.min(parseInt(args[1]), chunkedCommands.length)) : 1
await Player.Tell(Utils.formatString(Localization['COMMAND_LIST_PAGE'], {max: chunkedCommands.length, current: page}, '%')[0])
delay && await wait(300)
for (var i = 0; i < chunkedCommands[page - 1].length; i++) {
Player.Tell(`^7[^6${chunkedCommands[page - 1][i][0]}^7] ${Localization[`COMMAND_${chunkedCommands[page - 1][i][0].toLocaleUpperCase()}`]}`)
delay && await wait(300)
}
break
default:
var command = Utils.getCommand({...this.Manager.commands, ...this.Manager.Commands.Commands}, args[1])
if (!command) {
Player.Tell(Localization['COMMAND_NOT_FOUND'])
return
}
Player.Tell(`${Localization[`COMMAND_${command.toLocaleUpperCase()}`]}`)
delay && await wait(300)
Player.Tell(`Usage: ^5${config.commandPrefixes[0]}^7${Localization[`USAGE_${command.toLocaleUpperCase()}`]}`)
break
}
}
},
'fastrestart': {
ArgumentLength: 0,
Alias: 'fre',
Permission: Permissions.Commands.COMMAND_USER_CMDS,
inGame: true,
callback: async (Player, args) => {
if (await this.is_staff(Player) == false)
{
Player.Tell("^1Staff only")
return
}
await this.Server.Rcon.executeCommandAsync('fast_restart')
this.Server.Broadcast(Utils.formatString(Localization['COMMAND_FASTRESTART_FORMAT'], {Name: Player.Name}, '%'))
}
},
'maprestart': {
ArgumentLength: 0,
Alias: 'mr',
Permission: Permissions.Commands.COMMAND_USER_CMDS,
inGame: true,
callback: async (Player, args) => {
if (await this.is_staff(Player) == false)
{
Player.Tell("^1Staff only")
return
}
await this.Server.Rcon.executeCommandAsync('map_restart')
}
},
'maprotate': {
ArgumentLength: 0,
Alias: 'rotate',
Permission: Permissions.Commands.COMMAND_MAP,
inGame: true,
callback: async (Player, args) => {
await this.Server.Rcon.executeCommandAsync('map_rotate')
}
},
'map': {
ArgumentLength: 1,
Alias: 'm',
Permission: Permissions.Commands.COMMAND_MAP,
inGame: true,
callback: async (Player, args) => {
var delay = 3000
var Map = this.Server.getMap(args[1]) ? this.Server.getMap(args[1]) : {Name: args[1], Alias: args[1]}
this.Server.Broadcast(Utils.formatString(Localization['COMMAND_MAP_FORMAT'], {Name: Map.Alias, Delay: (delay / 1000).toFixed(0)}, '%')[0])
await wait(delay)
await this.Server.Rcon.executeCommandAsync(`map ${Map.Name}`)
}
},
'globalchat': {
ArgumentLength: 0,
Alias: 'gc',
Permission: Permissions.Commands.COMMAND_USER_CMDS,
inGame: true,
callback: async (Player) => {
if (!Player.Session) return
Player.Session.Data.globalChat = !Player.Session.Data.globalChat
Player.Tell(Localization[`COMMAND_GLOBALCHAT_${Player.Session.Data.globalChat.toString().toLocaleUpperCase()}`])
}
},
'nextmap': {
ArgumentLength: 0,
Alias: 'nm',
Permission: Permissions.Commands.COMMAND_USER_CMDS,
inGame: true,
callback: async (Player, args) => {
var mapIndex = this.Server.mapRotation.indexOf(this.Server.mapRotation.find(Map => Map == this.Server.Mapname))
var nextMap = mapIndex < this.Server.mapRotation.length - 1 ? this.Server.mapRotation[mapIndex + 1] : this.Server.mapRotation[0]
nextMap = this.Server.getMap(nextMap) ? this.Server.getMap(nextMap).Alias : nextMap
if (mapIndex < 0 || !nextMap) {
Player.Tell(Localization['COMMAND_NEXTMAP_NOT_FOUND'])
return
}
Player.Tell(Utils.formatString(Localization['COMMAND_NEXTMAP_FORMAT'], {Name: nextMap}, '%'))
}
},
'links': {
ArgumentLength: 0,
Permission: Permissions.Commands.COMMAND_USER_CMDS,
inGame: false,
callback: async (Player, args) => {
if (!config.links || !config.links.length) {
Player.Tell(Localization['COMMAND_LINKS_NOT_CONFIG'])
}
if (args[1]) {
var found = false
config.links.forEach(link => {
if (found) return
if (link.Name.toLocaleLowerCase().startsWith(args[1].toLocaleLowerCase())) {
Player.Tell(Utils.formatString(Localization['COMMAND_LINKS_FORMAT'], link, '%')[0])
found = true
}
})
!found && Player.Tell(Localization['COMMAND_LINKS_NOT_FOUND'])
return
}
for (var i = 0; i < config.links.length; i++) {
Player.Tell(Utils.formatString(Localization['COMMAND_LINKS_FORMAT'], config.links[i], '%')[0])
await wait(500)
}
}
},
'ping': {
ArgumentLength: 0,
Permission: Permissions.Commands.COMMAND_USER_CMDS,
inGame: false,
callback: function (Player) {
Player.Tell('pong')
}
},
'broadcast': {
ArgumentLength: 1,
Permission: Permissions.Commands.COMMAND_MAP,
inGame: false,
callback: async (Player, args) => {
this.Managers.forEach(Manager => {
Manager.Server.Broadcast(`^7[^1Broadcast ^7(^5${Player.Name}^7)] ${args.slice(1).join(' ')}`)
})
Player.Tell(`^1Broadcasted^7: ${args.slice(1).join(' ')}`)
}
},
'tell': {
ArgumentLength: 2,
Permission: Permissions.Commands.COMMAND_USER_CMDS,
inGame: false,
callback: async (Player, args = null, delay) => {
var Client = await this.Server.getClient(args[1])
switch (true) {
case (!Client):
Player.Tell(Localization.COMMAND_CLIENT_NOT_FOUND)
return
}
var Target = this.Server.findClient(Client.ClientId)
switch (true) {
case (!Target):
Player.Tell(Localization.COMMAND_CLIENT_NOT_INGAME)
return
}
Target.Session && (Target.Session.Data.lastMsg = Player)
Player.inGame && (Player.Session.Data.lastMsg = Target)
Target.Tell(`^3[^5${Player.Name}^3 (@^5${Player.ClientId}^3) -> me]^7 ${args.slice(2).join(' ')}`)
Player.Tell(`^3[me -> ^5${Target.Name} ^3(@^5${Target.ClientId}^3)^3]^7 ${args.slice(2).join(' ')}`)
}
},
'reply': {
ArgumentLength: 1,
Permission: Permissions.Commands.COMMAND_USER_CMDS,
Alias: 'r',
inGame: true,
callback: async(Player, args) => {
switch (true) {
case (!Player.Session || !Player.Session.Data.lastMsg):
Player.Tell(Localization['COMMAND_REPLY_NOT_CONV'])
return
case (!this.Server.findClient(Player.Session.Data.lastMsg.ClientId)):
Player.Tell(Localization['COMMAND_CLIENT_NOT_INGAME'])
return
}
Player.Session.Data.lastMsg.Tell(`^3[^5${Player.Name}^3 (@^5${Player.ClientId}^3) -> me]^7 ${args.slice(1).join(' ')}`)
Player.Tell(`^3[me -> ^5${Player.Session.Data.lastMsg.Name} ^3(@^5${Player.Session.Data.lastMsg.ClientId}^3)^3]^7 ${args.slice(1).join(' ')}`)
}
},
'players': {
ArgumentLength: 0,
Permission: Permissions.Commands.COMMAND_USER_CMDS,
inGame: false,
callback: async (Player, args = null, delay) => {
var allClients = Utils.chunkArray(this.getAllClients(), Player.inGame ? 4 : 15)
var page = Number.isInteger(parseInt(args[1])) ? Math.max(1, Math.min(parseInt(args[1]), allClients.length)) : 1
if (!allClients.length) {
Player.Tell(Localization['NO_PLAYERS_ONLINE'])
return
}
await Player.Tell(Utils.formatString(Localization['COMMAND_LIST_PAGE'], {max: allClients.length, current: page}, '%')[0])
for (var i = 0; i < allClients[page - 1].length; i++) {
Player.Tell(Utils.formatString(Localization['COMMAND_PLAYERS_FORMAT'],
{
Name: allClients[page - 1][i].Name,
ClientId: allClients[page - 1][i].ClientId,
Role: Utils.getRoleFrom(allClients[page - 1][i].PermissionLevel, 1).Name,
Level: allClients[page - 1][i].PermissionLevel,
Hostname: allClients[page - 1][i].Server.HostnameRaw
}, '%')[0])
}
}
},
'info': {
ArgumentLength: 0,
Permission: Permissions.Commands.COMMAND_USER_CMDS,
inGame: false,
callback: (Player) => {
Player.Tell(`Node Server Manager - v${this.Manager.Version} by ${this.Manager.Author}`)
}
},
'whoami': {
ArgumentLength: 0,
Permission: Permissions.Commands.COMMAND_USER_CMDS,
inGame: false,
callback: async (Player) => {
var info = await this.Server.DB.getClient(Player.ClientId)
if (!info) {
Player.Tell(Localization['COMMAND_CLIENT_NOT_FOUND'])
return
}
Player.Tell(`[^5${info.Name}^7] [@^5${info.ClientId}^7] [^5${Utils.getRoleFrom(Math.min(info.PermissionLevel, 5), 1).Name}^7] [^5${info.IPAddress}^7] [^5${info.Guid}^7]`)
}
},
'whois': {
ArgumentLength: 1,
Permission: 'ROLE_ADMIN',
inGame: false,
callback: async (Player, args) => {
var Client = await this.Server.getClient(args[1])
switch (true) {
case (!Client):
Player.Tell(Localization['COMMAND_CLIENT_NOT_FOUND'])
return
}
var info = await this.Server.DB.getClient(Client.ClientId)
Player.Tell(`[^5${info.Name}^7] [@^5${info.ClientId}^7] [^5${Utils.getRoleFrom(Math.min(info.PermissionLevel, 5), 1).Name}^7] [^5${info.IPAddress}^7] ^7[^5${info.Guid}^7]`)
}
},
'testperm': {
ArgumentLength: 1,
Permission: Permissions.Commands.COMMAND_USER_CMDS,
inGame: false,
callback: async (Player, args) => {
var Permission = Utils.getRoleFrom(args.slice(1).join(' '), 0)
var Client = await this.Server.DB.getClient(Player.ClientId)
switch (true) {
case (!Client):
Player.Tell(Localization['COMMAND_CLIENT_NOT_FOUND'])
return
case (!Permission):
Player.Tell(Localization['ROLE_NOT_EXIST'])
return
case (Client.PermissionLevel < Permissions.Levels.ROLE_ADMIN):
Player.Tell(Localization['COMMAND_FORBIDDEN'])
return
case (Client.PermissionLevel < Permission.Level):
Player.Tell(Localization['ROLE_HIERARCHY_ERROR'])
return
}
Player.PermissionLevel = Permission.Level
Player.Tell(`Permissions set to [ ^5${Permission.Name}^7 ]`)
}
},
'servers': {
ArgumentLength: 0,
Permission: Permissions.Commands.COMMAND_USER_CMDS,
inGame: false,
callback: async (Player, args, delay) => {
var Managers = this.Managers.concat()
if (args[1] && Managers[parseInt(args[1])] && Managers[parseInt(args[1])].Server.Mapname) {
var Manager = Managers[parseInt(args[1])]
Player.Tell(Utils.formatString(Localization['COMMAND_SERVERS_FORMAT'],
{
Id: Manager.Server.Id,
Hostname: Manager.Server.Hostname,
Host: Manager.Server.getAddress(),
Clients: Manager.Server.getClients().length,
MaxClients: Manager.Server.MaxClients,
Mapname: Manager.Server.getMapname().Alias
}, '%'))
return
}
for (var i = 0; i < Managers.length; i++) {
var Manager = Managers[i]
if (!Manager.Server.Mapname) continue
Player.Tell(Utils.formatString(Localization['COMMAND_SERVERS_FORMAT'],
{
Id: Manager.Server.Id,
Hostname: Manager.Server.Hostname,
Host: Manager.Server.getAddress(),
Clients: Manager.Server.getClients().length,
MaxClients: Manager.Server.MaxClients,
Mapname: Manager.Server.getMapname().Alias
}, '%'))
delay && await wait(500)
}
}
},
'token': {
ArgumentLength: 0,
Permission: Permissions.Commands.COMMAND_USER_CMDS,
inGame: false,
callback: async (Player) => {
var Client = await this.Server.DB.getClient(Player.ClientId)
switch (true) {
case (Player.discordUser):
Player.Tell(Localization['COMMAND_ENV_ERROR'])
return
case (!Client):
Player.Tell(Localization['COMMAND_CLIENT_NOT_FOUND'])
return
case (!Client.Settings.TokenLogin):
Player.Tell(Localization['TOKEN_LOGIN_DISABLED'])
return
}
var rawToken = crypto.randomBytes(3).toString('hex').toLocaleUpperCase();
rawToken = rawToken.split('')
var formattedToken = []
rawToken.forEach(char => {
if (Number.isInteger(parseInt(char))) {
formattedToken.push(`^5${char}^7`)
} else {
formattedToken.push(`^3${char}^7`)
}
})
Player.Tell(Localization.COMMAND_TOKEN_FORMAT
.replace('%CLIENTID%', Player.ClientId)
.replace('%TOKEN%', formattedToken.join('')))
await this.Server.DB.createToken(Player.ClientId, rawToken.join(''))
}
},
'rcon': {
ArgumentLength: 1,
Permission: Permissions.Commands.COMMAND_RCON,
inGame: false,
callback: async (Player, args, delay) => {
var result = []
if (!Player.inGame) {
switch (true) {
case (args.length < 2):
Player.Tell(Localization.RCON_SERVER_NOT_SPECIFIED)
return
case (!this.Managers[parseInt(args[1])] || !this.Managers[parseInt(args[1])].Server.Mapname || !this.Managers[parseInt(args[1])].Server.Rcon.isRunning):
Player.Tell(Localization.SERVER_NOT_EXIST)
return
}
var cmd = (await this.Managers[parseInt(args[1])].Server.Rcon.executeCommandAsync(args.slice(2).join(' ')))
result = cmd ? cmd.trim().split('\n') : Localization['COMMAND_RCON_FAILED'].split('\n')
} else {
var cmd = await this.Server.Rcon.executeCommandAsync(args.slice(1).join(' '))
result = cmd ? cmd.trim().split('\n') : Localization['COMMAND_RCON_FAILED'].split('\n')
}
result[0] = Localization.COMMAND_EXECUTE_SUCCESS
for (var i = 0; i < result.length; i++) {
Player.Tell(result[i])
delay && await wait(300)
}
}
},
'setrole': {
ArgumentLength: 2,
Permission: Permissions.Commands.COMMAND_SETROLE,
inGame: false,
Alias: 'sr',
callback: async (Player, args) => {
var Role = args.slice(2).join(' ')
var Client = await this.Server.getClient(args[1])
var Permission = Utils.getRoleFrom(Role, 0)
switch (true) {
case (!Client):
Player.Tell(Localization.COMMAND_CLIENT_NOT_FOUND)
return
case (!Permission):
Player.Tell(Localization.ROLE_NOT_EXIST)
return
case (Permission.Level >= Player.PermissionLevel):
Player.Tell(Localization.ROLE_HIERARCHY_ERROR)
return
case (Player.ClientId == Client.ClientId):
Player.Tell(Localization.ROLE_SELF_ERROR)
return
}
var Target = this.Server.findClient(Client.ClientId)
if (Target) {
Target.PermissionLevel = Permission.Level
Target.Tell(`Your role has been set to [ ^5${Permission.Name}^7 ]`)
var role = Permission.Name
var customTag = await this.Server.DB.metaService.getPersistentMeta('custom_tag', Target.ClientId)
role = customTag ? customTag.Value : Utils.stripString(role)
Target.Server.Rcon.executeCommandAsync(`setclantagraw ${Target.Clientslot} "${role}"`)
}
this.Server.DB.setLevel(Client, Permission.Level)
Player.Tell(`^5${Client.Name}^7's role has been set to [ ^5${Permission.Name}^7 ]`)
}
},
'owner': {
Permission: Permissions.Commands.COMMAND_USER_CMDS,
inGame: true,
callback: async (Player) => {
var Owner = await this.Server.DB.getOwner()
switch (true) {
case !Owner:
this.Server.DB.setLevel(Player, Permissions.Levels['ROLE_OWNER'])
Player.PermissionLevel = Permissions.Levels['ROLE_OWNER']
Player.Tell(`Your role has been set to [ ^5${Utils.getRoleFrom(5, 1).Name}^7 ]`)
return
case (Owner.ClientId == Player.ClientId):
Player.Tell(`You're already the owner!`)
return
case (Owner.ClientId != Player.ClientId):
Player.Tell(`^5${(await this.Server.DB.getClient(Owner.ClientId)).Name}^7 owns this server`)
return
}
}
},
'kick': {
ArgumentLength: 2,
Alias: 'k',
Permission: Permissions.Commands.COMMAND_USER_CMDS,
inGame: false,
callback: async (Player, args) => {
if (await this.is_staff(Player) == false)
{
Player.Tell("^1Staff only")
return
}
var Client = await this.Server.getClient(args[1])
switch (true) {
case (!Client):
Player.Tell(Localization.COMMAND_CLIENT_NOT_FOUND)
return
/* case (Client.PermissionLevel >= Player.PermissionLevel):
Player.Tell(Localization.CLIENT_HIERARCHY_ERROR)*/
return
}
var Target = this.Server.findClient(Client.ClientId)
Target ? ( Player.Tell(`^5${Target.Name}^7 was kicked`), Target.Kick(`${args.slice(2).join(' ')}`, Player)) : Player.Tell(Localization.COMMAND_CLIENT_NOT_INGAME)
}
},
'unban': {
ArgumentLength: 2,
Alias: 'ub',
Permission: Permissions.Commands.COMMAND_USER_CMDS,
inGame: false,
callback: async (Player, args) => {
if (await this.is_staff(Player) == false)
{
Player.Tell("^1Staff only")
return
}
var Client = await this.Server.getClient(args[1])
var Reason = args.slice(2).join(' ')
/*switch (true) {
case (Client.PermissionLevel >= Player.PermissionLevel):
Player.Tell(Localization.CLIENT_HIERARCHY_ERROR)
return
}*/
var count = await this.Server.DB.unbanClient(Client.ClientId, Reason, Player.ClientId)
this.Server.DB.addPenalty({
TargetId: Client.ClientId,
OriginId: Player.ClientId,
PenaltyType: 'PENALTY_UNBAN',
Active: false,
Duration: 0,
Reason: Reason
})
if (count) {
Player.Tell(`Unbanned ^5${Client.Name}^7 for ^5${Reason}^7`)
this.Server.emit('penalty', 'PENALTY_UNBAN', Client, Reason, Player)
} else
Player.Tell(`^5${Client.Name}^7 is not banned`)
}
},
'tempban': {
ArgumentLength: 3,
Alias: 'tb',
Permission: Permissions.Commands.COMMAND_USER_CMDS,
inGame: false,
callback: async (Player, args) => {
if (await this.is_staff(Player) == false)
{
Player.Tell("^1Staff only")
return
}
var timeVars = {
'd': 86400,
'h': 3600,
'm': 60,
's': 1,
}
var Client = await this.Server.getClient(args[1])
if (!args[2].match(/([0-9]+)([A-Za-z]+)/)) {
Player.Tell(Localization.COMMAND_PARSE_TIME_ERROR)
return
}
var parts = Array.from(args[2].match(/([0-9]+)([A-Za-z]+)/)).slice(1)
switch (true) {
case (!Client):
Player.Tell(Localization['COMMAND_CLIENT_NOT_FOUND'])
return
/* case (Client.PermissionLevel >= Player.PermissionLevel):
Player.Tell(Localization['CLIENT_HIERARCHY_ERROR'])*/
return
case (!parts || parts.length < 2 || !timeVars[parts[1]] || !Number.isInteger(parseInt(parts[0]))):
Player.Tell(Localization['COMMAND_PARSE_TIME_ERROR'])
return
}
var Reason = args.slice(3).join(' ')
var Duration = parseInt(parts[0] * timeVars[parts[1]])
Reason = Reason.replace(new RegExp(/rule([0-9]+)/g), (rule) => {
var num = Math.max(parseInt(rule.substr(4)), 1) - 1
if (this.Server.config.rules[num]) {
return this.Server.config.rules[num]
}
return rule
})
if (Duration > 86400 * 32) {
Player.Tell(Localization['COMMAND_PARSE_TIME_ERROR'])
return
}
var Target = this.Server.findClient(Client.ClientId)
if (Target) {
Target.Tempban(Reason, Player, Duration)
Player.Tell(`Banned ^5${Client.Name}^7 for ^5${Duration}^7 seconds for ^5${Reason}^7`)
return
}
this.Server.DB.addPenalty({
TargetId: Client.ClientId,
OriginId: Player.ClientId,
PenaltyType: 'PENALTY_TEMP_BAN',
Duration: Duration,
Reason: Reason
})
this.Server.emit('penalty', 'PENALTY_TEMP_BAN', Client, Reason, Player, Duration)
Player.Tell(`Banned ^5${Client.Name}^7 for ^5${Duration}^7 seconds for ^5${Reason}^7`)
}
},
'ban': {
ArgumentLength: 2,
Alias: 'b',
Permission: Permissions.Commands.COMMAND_USER_CMDS,
inGame: false,
callback: async (Player, args) => {
var Client = await this.Server.getClient(args[1])
if (await this.is_staff(Player) == false)
{
Player.Tell("^1Staff only")
return
}
switch (true) {
case (!Client):
Player.Tell(Localization.COMMAND_CLIENT_NOT_FOUND)
return
/* case (Client.PermissionLevel >= Player.PermissionLevel):
Player.Tell(Localization.CLIENT_HIERARCHY_ERROR)*/
return
}
var Reason = args.slice(2).join(' ')
Reason = Reason.replace(new RegExp(/rule([0-9]+)/g), (rule) => {
var num = Math.max(parseInt(rule.substr(4)), 1) - 1
if (this.Server.config.rules[num]) {
return this.Server.config.rules[num]
}
return rule
})
var Target = this.Server.findClient(Client.ClientId)
if (Target) {
Target.Ban(Reason, Player)
Player.Tell(`Banned ${Target.Name} permanently for ${Reason}`)
return
}
this.Server.DB.addPenalty({
TargetId: Client.ClientId,
OriginId: Player.ClientId,
PenaltyType: 'PENALTY_PERMA_BAN',
Duration: 0,
Reason: Reason
})
this.Server.emit('penalty', 'PENALTY_PERMA_BAN', Client, Reason, Player)
Player.Tell(`Banned ${Client.Name} permanently for ${Reason}`)
}
},
'find': {
ArgumentLength: 1,
Alias: 'f',
Permission: Permissions.Commands.COMMAND_USER_CMDS,
inGame: false,
callback: async (Player, args, delay) => {
var MatchedClients = await this.Server.DB.getClientByName(args.slice(1).join(' '), 10)
if (MatchedClients.length <= 0) {
Player.Tell(Localization['COMMAND_CLIENT_NOT_FOUND'])
return
}
for (var i = 0; i < Math.min(MatchedClients.length, 10); i++) {
Player.Tell(`^5${MatchedClients[i].Name} ^7| ^5@${MatchedClients[i].ClientId} ^7| ^5${Utils.getRoleFrom(MatchedClients[i].PermissionLevel, 1).Name} ^7| Active ${moment(MatchedClients[i].LastConnection).calendar()} | Joined ${moment(MatchedClients[i].FirstConnection).calendar()}`)
delay && await wait(300)
}
}
}
}
this.Server.on('event', this.onEventAsync.bind(this));
}
getAllClients() {
var Clients = []
this.Managers.forEach(Manager => {
var clients = Manager.Server.Clients.filter(x => x)
Clients = Clients.concat(clients)
})
return Clients
}
async playerCommand (Player, args, prefix) {
try {
if (!Player) return
var Client = await this.Server.DB.getClient(Player.ClientId)
if (Client.Settings && Client.Settings.InGameLogin && !Player.Session.Data.Authorized) {
Player.Tell(Localization['CLIENT_NOT_AUTHORIZED'])
return
}
var isBroadcast = config.broadcastCommandPrefixes.includes(prefix)
var executedMiddleware = await this.Manager.Commands.executeMiddleware(args[0], Player, args, { broadcast: isBroadcast })
if (await this.Manager.Commands.execute(args[0], Player, args, { broadcast: isBroadcast })) return
var command = Utils.getCommand(this.Manager.commands, args[0])
switch (true) {
case (!this.Manager.commands[command]):
case (this.Manager.commands[command].gameTypeExclusions && this.Manager.commands[command].gameTypeExclusions.includes(this.Server.Gametype)):
!executedMiddleware && Player.Tell(Localization.COMMAND_NOT_FOUND)
return
case (Client.Settings && Client.Settings.InGameLogin && !Player.Session.Data.Authorized):
Player.Tell(Localization.CLIENT_NOT_AUTHORIZED)
return
case (Player.PermissionLevel < Permissions.Levels[this.Manager.commands[command].Permission]):
Player.Tell(Localization.COMMAND_FORBIDDEN)
return
case (args.length - 1 < this.Manager.commands[command].ArgumentLength):
Player.Tell(Localization.COMMAND_ARGUMENT_ERROR)
await wait(300)
Player.Tell(`Usage: ^6${config.commandPrefixes[0]}^7${Localization[`USAGE_${command.toLocaleUpperCase()}`]}`)
return
}
this.Manager.commands[command].logToAudit != false && this.Server.DB.logActivity(`@${Player.ClientId}`, Localization['AUDIT_CMD_EXEC'].replace('%NAME%', command), args.join(' '))
this.Manager.commands[command].callback(Player, args, true)
}
catch (e) {
if (process.env.NODE_ENV && process.env.NODE_ENV.toLocaleLowerCase() == 'dev')
console.log(e)
Player.Tell(Localization['COMMAND_ERROR'])
}
}
async is_staff(Player)
{
for (var i = 0; i < this.staff_list_a.length; i++)
if (this.staff_list_a[i] == Player.Guid)
return true
Player.Tell("hehe boi u tryna scam da kiels by using staff cmd? oh helllll no fk emp")
return false
}
}
module.exports = Plugin

View file

@ -0,0 +1,46 @@
const path = require('path')
const { Command, NodeServerManager } = require(path.join(__dirname, `../Lib/Classes.js`))
const Utils = new (require(path.join(__dirname, '../Utils/Utils.js')))()
const Permissions = require(path.join(__dirname, `../Configuration/NSMConfiguration.json`)).Permissions
const Localization = require(path.join(__dirname, `../Configuration/Localization-${process.env.LOCALE}.json`)).lookup
class Plugin {
constructor(Server, Manager) {
this.Server = Server
this.Manager = Manager
this.Server.on('connect', this.onPlayerConnected.bind(this))
}
async onPlayerConnected(Player) {
if (this.Server.getClients().length + this.Server.reservedSlots > this.Server.MaxClients && Player.PermissionLevel < Permissions.Levels['ROLE_MODERATOR']) {
Player.Kick(Localization['KICK_CLIENTSLOT_RESERVED'])
}
try {
var playerPenalties = await this.Server.DB.getAllPenalties(Player.ClientId)
for (var i = 0; i < playerPenalties.length; i++) {
switch (playerPenalties[i].PenaltyType) {
case 'PENALTY_PERMA_BAN':
if (playerPenalties[i].Active) {
Player.Kick(`Banned for: ^5${playerPenalties[i].Reason}`, NodeServerManager)
return
}
break
case 'PENALTY_TEMP_BAN':
var dateDiff = (new Date(playerPenalties[i].Date) - new Date()) / 1000
if (dateDiff + playerPenalties[i].Duration > 0) {
if (playerPenalties[i].Active) {
Player.Kick(`Banned for: ^5${playerPenalties[i].Reason}^7 ${Utils.secondsToDhms(dateDiff + playerPenalties[i].Duration)} left`, NodeServerManager)
return
}
}
break
}
}
}
catch (e) { }
}
}
module.exports = Plugin

View file

@ -0,0 +1,50 @@
const ejs = require('ejs')
const config = JSON.parse(process.env.config)
const btoa = require('btoa')
const DiscordOauth2 = require("discord-oauth2")
const oauth = new DiscordOauth2()
module.exports = (app, db, Webfront) => {
if (!config.discordOAuth2Url || !config.discordClientId || !config.discordSecret) return
const redirect = config.discordOAuth2Url
app.get('/api/discord/callback', async (req, res, next) => {
if (!req.session.ClientId || !req.query.code) {
res.redirect('/')
return
}
var response = await oauth.tokenRequest({
clientId: config.discordClientId,
clientSecret: config.discordSecret,
code: req.query.code,
scope: ['identify', 'guilds'],
grantType: 'authorization_code',
redirectUri: redirect,
})
var user = await oauth.getUser(response.access_token)
Webfront.db.metaService.addPersistentMeta('discord_user', JSON.stringify(user), req.session.ClientId)
Webfront.db.metaService.addPersistentMeta('discord_id', user.id, req.session.ClientId)
res.redirect('/settings')
})
app.get('/api/discord/disconnect', async (req, res, next) => {
if (!req.session.ClientId) {
res.redirect('/')
return
}
Webfront.db.metaService.deletePersistentMeta('discord_user', req.session.ClientId)
Webfront.db.metaService.deletePersistentMeta('discord_id', req.session.ClientId)
res.redirect('/settings')
})
app.get('/api/discord/login', async (req, res, next) => {
res.redirect(`https://discordapp.com/api/oauth2/authorize?client_id=${config.discordClientId}&scope=identify&response_type=code&redirect_uri=${encodeURIComponent(redirect)}`)
})
}

View file

@ -0,0 +1,28 @@
const path = require('path')
const ejs = require('ejs')
const config = require(path.join(__dirname, `../../Configuration/NSMConfiguration.json`))
module.exports = (app, db, Webfront) => {
if (!config.socialMedia) return
var validLinks = config.socialMedia.filter(link => link[1].toString().match(/https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/g))
app.get('/links', async (req, res, next) => {
var header = await Webfront.renderDynamicHTML(req)
res.setHeader('Content-type', 'html')
ejs.renderFile(path.join(__dirname, '../../Webfront/html/links.ejs'), {
header, validLinks
}, (err, str) => {
res.end(str)
})
})
validLinks.forEach(link => {
app.get(`/${link[0].toString()}`, async (req, res, next) => {
res.status(301).redirect(link[1].toString())
})
})
Webfront.addHeaderHtml(`<a href='/links' class='wf-header-link'><i class="fas fa-link"></i></a>`, 5)
}

View file

@ -0,0 +1,41 @@
const path = require('path')
const ejs = require('ejs')
const Permissions = require(path.join(__dirname, `../../Configuration/NSMConfiguration.json`)).Permissions
const config = require(path.join(__dirname, `../../Configuration/NSMConfiguration.json`))
const jsdom = new require('jsdom')
module.exports = (app, db, Webfront) => {
app.get('/api/zstats', async (req, res, next) => {
var page = req.query.page ? req.query.page : 0
var limit = 10
var Stats = await db.getTopZStats(page, limit)
for (var i = 0; i < Stats.length; i++) {
delete Stats[i].Id
}
res.end(JSON.stringify(Stats))
})
app.get('/zstats', async (req, res, next) => {
var Client = req.session.ClientId ? await db.getClient(req.session.ClientId) : {Name: 'Guest', ClientId: 0}
var Motd = config.MOTD ? config.MOTD.replace('{USERNAME}', Client.Name)
.replace('{CLIENTID}', Client.ClientId) : null
res.setHeader('Content-type', 'text/html')
var Stats = await db.getTopZStats(0, 10)
var header = null
ejs.renderFile(path.join(__dirname, '../../Webfront/html/header.ejs'), {session: req.session, Permissions: Permissions, Motd: Motd, Client: Client, config: config}, (err, str) => {
var dom = new jsdom.JSDOM(str)
for (var i = 0; i < Webfront.headerExtraHtml.length; i++) {
var el = dom.window.document.createElement('div')
el.innerHTML = Webfront.headerExtraHtml[i].html
dom.window.document.getElementById('header-btns').insertBefore(el.firstChild, dom.window.document.getElementById('header-btns').children[Webfront.headerExtraHtml[i].index])
}
header = dom.window.document.getElementById('wf-header').outerHTML
})
ejs.renderFile(path.join(__dirname, '../../Webfront/html/zstats.ejs'), {header: header, Stats: Stats}, (err, str) => {
res.end(str)
})
})
}

View file

@ -0,0 +1,10 @@
var fs = require('fs')
module.exports = (app, db, Webfront) => {
fs.readdirSync(__dirname).forEach(function(file) {
if (file == "index.js") return
var name = file.substr(0, file.indexOf('.'))
require('./' + name)(app, db, Webfront)
})
}

View file

@ -0,0 +1,269 @@
const path = require('path')
const { Command } = require(path.join(__dirname, `../Lib/Classes.js`))
const Localization = require(path.join(__dirname, `../Configuration/Localization-${process.env.LOCALE}.json`)).lookup
const Utils = new (require(path.join(__dirname, '../Utils/Utils.js')))()
class Entity {
constructor(Server, entnum) {
this.entnum = entnum
this.Server = Server
}
async call(name, ..._args) {
const args = [..._args]
var buffer = `level.getentbynum(${this.entnum}).${name}(`
for (var i = 0; i < args.length; i++) {
switch (typeof args[i]) {
case 'string':
buffer += `\\"${args[i]}\\"`
break
case 'object':
buffer += args[i].code
break
default:
buffer += args[i]
break
}
if (i < args.length - 1) {
buffer += ', '
}
}
buffer += ')'
await this.Server.Rcon.executeCommandAsync(`chai_eval ${buffer}`)
}
format(name, ..._args) {
const args = [..._args]
var buffer = `level.getentbynum(${this.entnum}).${name}(`
for (var i = 0; i < args.length; i++) {
switch (typeof args[i]) {
case 'string':
buffer += `\\"${args[i]}\\"`
break
case 'object':
buffer += args[i].code
break
default:
buffer += args[i]
break
}
if (i < args.length - 1) {
buffer += ', '
}
}
buffer += ')'
return {code: buffer}
}
}
const scripting = {
entity: (Server, entnum) => {
return new Entity(Server, entnum)
},
vector: (arr) => {
return {
code: `[${parseFloat(arr[0])},${parseFloat(arr[1])},${parseFloat(arr[2])}]`
}
},
call: async (Server, name, ..._args) => {
const args = [..._args]
var buffer = `gsc.${name}(`
for (var i = 0; i < args.length; i++) {
switch (typeof args[i]) {
case 'string':
buffer += `\\"${args[i]}\\"`
break
case 'object':
buffer += args[i].code
break
default:
buffer += args[i]
break
}
if (i < args.length - 1) {
buffer += ', '
}
}
buffer += ')'
await Server.Rcon.executeCommandAsync(`chai_eval ${buffer}`)
}
}
class Plugin {
constructor(Server, Manager, Managers) {
this.Server = Server
this.Manager = Manager
this.Managers = Managers
this.Server.on('dvars_loaded', this.init.bind(this))
}
init() {
if (this.Server.Gamename != 'IW5') {
return
}
this.Manager.Commands.add(
new Command({
permission: 'ROLE_ADMIN'
})
.setName('kill')
.addParam({
index: 0,
name: 'target',
join: true
})
.addCallback(async (Player, params) => {
const Target = this.Server.findLocalClient(params.target)
if (!Target || !Target.Server || Target.Server.Id != Player.Server.Id) {
Player.Tell(Localization['COMMAND_CLIENT_NOT_FOUND'])
return
}
scripting.entity(this.Server, Target.Clientslot).call('suicide')
})
)
this.Manager.Commands.add(
new Command({
permission: 'ROLE_ADMIN'
})
.setName('give')
.addParam({
index: 0,
name: 'target',
join: false
})
.addParam({
index: 1,
name: 'weapon',
join: false
})
.addCallback(async (Player, params) => {
const Target = this.Server.findLocalClient(params.target)
if (!Target || !Target.Server || Target.Server.Id != Player.Server.Id) {
Player.Tell(Localization['COMMAND_CLIENT_NOT_FOUND'])
return
}
const weaponName = params.weapon.replace(new RegExp(/(\\|\")/g), '')
const entity = scripting.entity(this.Server, Target.Clientslot)
await entity.call('giveweapon', weaponName)
await entity.call('switchtoweapon', weaponName)
})
)
this.Manager.Commands.add(
new Command({
permission: 'ROLE_ADMIN'
})
.setName('tp')
.addParam({
index: 0,
name: 'target',
join: true
})
.addCallback(async (Player, params) => {
const Target = this.Server.findLocalClient(params.target)
if (!Target || !Target.Server || Target.Server.Id != Player.Server.Id) {
Player.Tell(Localization['COMMAND_CLIENT_NOT_FOUND'])
return
}
Player.Tell(Utils.formatString(Localization['COMMAND_TP_FORMAT'], {
target: Target.Name,
origin: 'you',
coords: ''
}))
const entity = scripting.entity(this.Server, Player.Clientslot)
const target = scripting.entity(this.Server, Target.Clientslot)
entity.call('setorigin', target.format('getorigin'))
})
)
this.Manager.Commands.add(
new Command({
permission: 'ROLE_ADMIN'
})
.setName('tphere')
.addParam({
index: 0,
name: 'target',
join: true
})
.addCallback(async (Player, params) => {
const Target = this.Server.findLocalClient(params.target)
if (!Target || !Target.Server || Target.Server.Id != Player.Server.Id) {
Player.Tell(Localization['COMMAND_CLIENT_NOT_FOUND'])
return
}
Player.Tell(Utils.formatString(Localization['COMMAND_TP_FORMAT'], {
target: 'you',
origin: Target.Name,
coords: ''
}))
const entity = scripting.entity(this.Server, Player.Clientslot)
const target = scripting.entity(this.Server, Target.Clientslot)
target.call('setorigin', entity.format('getorigin'))
})
)
this.Manager.Commands.add(
new Command({
permission: 'ROLE_ADMIN'
})
.setName('setvelocity')
.addParam({
index: 0,
name: 'target',
join: false
})
.addParam({
index: 1,
name: 'velocity',
join: false
})
.addCallback(async (Player, params) => {
const Target = this.Server.findLocalClient(params.target)
if (!Target || !Target.Server || Target.Server.Id != Player.Server.Id) {
Player.Tell(Localization['COMMAND_CLIENT_NOT_FOUND'])
return
}
const entity = scripting.entity(this.Server, Player.Clientslot)
const velocity = params.velocity.split(',').map(f => parseFloat(f))
const vector = [0.0, 0.0, 0.0]
for (var i = 0; i < Math.min(velocity.length, 3); i++) {
vector[i] = velocity[i]
}
entity.call('setvelocity', scripting.vector(vector))
})
)
}
}
module.exports = Plugin

View file

@ -0,0 +1,85 @@
const path = require('path')
const config = require(path.join(__dirname, `../Configuration/NSMConfiguration.json`))
class Plugin {
constructor(Server, Manager) {
this.Server = Server
this.Manager = Manager
this.Buffer = { Stats: {}, previousStats: {} }
setInterval(this.updateStats.bind(this), 300 * 1000)
this.init()
}
async playerConnected (Player) {
Player.lastSeen = new Date()
Player.on('death', async (Attacker, Attack) => {
this.Buffer.Stats[Player.ClientId] = this.Buffer.Stats[Player.ClientId]
? this.Buffer.Stats[Player.ClientId]
: await this.Server.DB.getPlayerStatsTotal(Player.ClientId)
this.Buffer.Stats[Attacker.ClientId] = this.Buffer.Stats[Attacker.ClientId]
? this.Buffer.Stats[Attacker.ClientId]
: await this.Server.DB.getPlayerStatsTotal(Attacker.ClientId)
!this.Buffer.previousStats[Player.ClientId] && (this.Buffer.previousStats[Player.ClientId] = {}, Object.assign(this.Buffer.previousStats[Player.ClientId], this.Buffer.Stats[Player.ClientId]))
!this.Buffer.previousStats[Attacker.ClientId] && (this.Buffer.previousStats[Attacker.ClientId] = {}, Object.assign(this.Buffer.previousStats[Attacker.ClientId], this.Buffer.Stats[Attacker.ClientId]))
this.Buffer.Stats[Player.ClientId].Deaths++
if (Attacker.Clientslot != Player.Clientslot) {
this.Buffer.Stats[Attacker.ClientId].Kills++
this.Buffer.Stats[Player.ClientId].TotalPerformance += this.Buffer.Stats[Attacker.ClientId].Performance - 400
this.Buffer.Stats[Attacker.ClientId].TotalPerformance += this.Buffer.Stats[Player.ClientId].Performance + 400
this.Buffer.Stats[Player.ClientId].Performance = (this.Buffer.Stats[Player.ClientId].TotalPerformance
+ (this.Buffer.Stats[Attacker.ClientId].Performance - 400))
/ (this.Buffer.Stats[Player.ClientId].Kills
+ this.Buffer.Stats[Player.ClientId].Deaths)
this.Buffer.Stats[Attacker.ClientId].Performance = (this.Buffer.Stats[Attacker.ClientId].TotalPerformance
+ (this.Buffer.Stats[Player.ClientId].Performance + 400))
/ (this.Buffer.Stats[Attacker.ClientId].Kills
+ this.Buffer.Stats[Attacker.ClientId].Deaths)
}
})
Player.on('message', async (Message) => {
if (Message.startsWith(config.commandPrefix)) return
await this.Server.DB.logMessage(Player.ClientId, Player.Name, Player.Server.HostnameRaw, Message)
})
}
async updateStats() {
Object.entries(this.Buffer.Stats).forEach(async Stats => {
if (!this.Buffer.previousStats[Stats[0]] || (Stats[1].Kills <= this.Buffer.previousStats[Stats[0]].Kills && Stats[1].Deaths <= this.Buffer.previousStats[Stats[0]].Deaths)) return
this.Server.DB.editStats(Stats[0], Stats[1])
this.Buffer.previousStats[Stats[0]] = {}
Object.assign(this.Buffer.previousStats[Stats[0]], Stats[1])
})
}
init () {
this.Server.on('connect', this.playerConnected.bind(this))
this.playedTimeLogger()
}
playedTimeLogger() {
setInterval(async () => {
if (!this.Server.Rcon.isRunning) return
this.Server.Clients.forEach(async Client => {
if (!Client || !Client.ClientId) return
this.Server.DB.incrementStat(Client.ClientId, (new Date() - Client.lastSeen) / 1000 / 60, 'PlayedTime')
Client.lastSeen = new Date()
var Stats = this.Buffer.Stats[Client.ClientId] ? this.Buffer.Stats[Client.ClientId] : await this.Server.DB.getPlayerStatsTotal(Client.ClientId)
this.Server.DB.addStatRecord(Client.ClientId, Stats.TotalPerformance, Math.max(0, Stats.Performance))
})
}, 60000)
}
}
module.exports = Plugin

View file

@ -0,0 +1,290 @@
const path = require('path')
const wait = require('delay')
const Permissions = require(path.join(__dirname, `../Configuration/NSMConfiguration.json`)).Permissions
const config = require(path.join(__dirname, `../Configuration/NSMConfiguration.json`))
const Localization = require(path.join(__dirname, `../Configuration/Localization-${process.env.LOCALE}.json`)).lookup
const Utils = new (require(path.join(__dirname, '../Utils/Utils.js')))()
const { Command, NodeServerManager } = require(path.join(__dirname, `../Lib/Classes.js`))
class Plugin {
constructor(Server, Manager) {
this.Server = Server
this.Manager = Manager
this.voteTime = 120
this.cooldownTime = 120
this.currentVote = {
Type: null,
Origin: null,
Target: null,
Votes: [],
callback: null
}
this.currentVoteDefault = {
Type: null,
Origin: null,
Target: null,
Votes: [],
callback: null
}
this.votingSystem()
}
async startVote() {
await wait(this.voteTime * 1000)
if (this.currentVote.Type) {
this.Server.Broadcast(Utils.formatString(Localization['COMMAND_VOTE_END'], { Action: this.currentVote.actionString }, '%')[0])
this.currentVote.Origin.cooldownStart = new Date()
this.currentVote = this.currentVoteDefault
}
}
minimumVotes() {
return this.Server.Clients.filter(x => x).length < 3 ? this.Server.Clients.filter(x => x).length : Math.ceil(this.Server.Clients.filter(x => x).length / 2)
}
hasVoted(Player) {
for (var i = 0; i < this.currentVote.Votes.length; i++) {
if (this.currentVote.Votes[i].ClientId == Player.ClientId) {
return true
}
}
return false
}
voteUpdate() {
if (this.currentVote.Type && this.currentVote.Votes.length >= this.minimumVotes()) {
this.currentVote.callback(this.currentVote)
this.currentVote = this.currentVoteDefault
}
}
async votingSystem() {
var voteTypes = {
Kick: {
Name: 'VOTE_KICK',
callback: async (Vote) => {
Vote.Target.Kick(Utils.formatString(Localization['COMMAND_VOTEKICK_KICK_MESSAGE'], {
origin: Vote.Origin.Name,
originId: Vote.Origin.ClientId,
reason: Vote.Reason
}, '%')[0], NodeServerManager)
}
},
Map: {
Name: 'VOTE_MAP',
callback: async (Vote) => {
var delay = 3000
this.Server.Broadcast(Utils.formatString(Localization['COMMAND_MAP_FORMAT'], {Name: Vote.Target.Alias, Delay: (delay / 1000).toFixed(0)}, '%')[0])
await wait(delay)
this.Server.Rcon.executeCommandAsync(`map ${Vote.Target.Name}`)
}
}
}
this.Manager.commands['stop'] = {
ArgumentLength: 0,
Alias: 'cancel',
Permission: Permissions.Commands.COMMAND_MAP,
inGame: true,
callback: async (Player, args) => {
switch (true) {
case (!this.currentVote.Type):
Player.Tell(Localization['COMMAND_VOTE_NO_VOTE'])
return
}
this.Server.Broadcast(Utils.formatString(Localization['COMMAND_VOTE_END'], { Action: this.currentVote.actionString }, '%')[0])
this.currentVote = this.currentVoteDefault
}
}
this.Manager.commands['yes'] = {
ArgumentLength: 0,
Permission: Permissions.Commands.COMMAND_USER_CMDS,
inGame: true,
callback: async (Player, args) => {
switch (true) {
case (!this.currentVote.Type):
Player.Tell(Localization['COMMAND_VOTE_NO_VOTE'])
return
case (this.hasVoted(Player)):
Player.Tell(Localization['COMMAND_VOTE_ALREADY_VOTED'])
return
}
this.currentVote.Votes.push(Player)
this.Server.Broadcast(Utils.formatString(Localization['COMMAND_VOTE_VOTED_TEMPLATE'], {
Name: Player.Name,
Prefix: config.commandPrefixes[0],
Action: this.currentVote.actionString,
Votes: this.currentVote.Votes.length,
minVotes: this.minimumVotes()
}, '%')[0])
this.voteUpdate()
}
}
this.Manager.commands['votekick'] = {
ArgumentLength: 2,
gameTypeExclusions: ['zclassic', 'zstandard'],
Alias: 'vk',
Permission: Permissions.Commands.COMMAND_USER_CMDS,
inGame: true,
callback: async (Player, args) => {
var Target = await this.Server.findLocalClient(args[1])
switch (true) {
case (args.slice(2).join(' ').length < 5):
Player.Tell(Utils.formatString(Localization['COMMAND_PARAM_LENGTH'], {
name: 'reason',
length: 5
}))
return
case (Player.Data.lastVote && (new Date() - Player.Data.lastVote) / 1000 < 300):
Player.Tell(Localization['VOTE_COMMANDS_COOLDOWN'])
return
case (!Target):
Player.Tell(Localization['COMMAND_CLIENT_NOT_FOUND'])
return
case (Target.PermissionLevel > Player.PermissionLevel):
case (Target.ClientId == Player.ClientId):
Player.Tell(Localization['COMMAND_VOTEKICK_HIERARCHY_ERR'])
return
case (Player.cooldownStart && new Date() - Player.cooldownStart > this.cooldownTime):
Player.Tell(Utils.formatString(Localization['COMMAND_VOTE_COOLDOWN'], { Time: (new Date() - Player.cooldownStart - this.cooldownTime) / 1000 }, '%')[0])
return
case (this.currentVote.Type && this.currentVote.Type != voteTypes.Kick.Name):
case (this.currentVote.Target && this.currentVote.Target.ClientId != Target.ClientId):
Player.Tell(Utils.formatString(Localization['COMMAND_VOTE_TYPE_ERR'], { Action: this.currentVote.actionString}, '%')[0])
return
case (this.hasVoted(Player)):
Player.Tell(Localization['COMMAND_VOTE_ALREADY_VOTED'])
return
}
if (!this.currentVote.Origin) {
Player.Data.lastVote = new Date()
Player.Data.lastVotes = Player.Data.lastVotes ? Player.Data.lastVotes : []
Player.Data.lastVotes.push(new Date())
var lastVotes = Player.Data.lastVotes.filter(date => (new Date() - date) / 1000 < 3600)
if (lastVotes.length >= 3) {
Player.Report(Utils.formatString(Localization['VOTEKICK_COMMNAD_REPORT'], {
player: Player.Name,
count: lastVotes.length
}, '%')[0])
}
this.currentVote = {
Origin: Player,
Target: Target,
Type: voteTypes.Kick.Name,
Reason: args.slice(2).join(' '),
Votes: [Player],
actionString: Utils.formatString(Localization['COMMAND_VOTEKICK_ACTION'], {Name: Target.Name, Reason: args.slice(2).join(' ')} , '%')[0],
callback: voteTypes.Kick.callback
}
this.Server.Broadcast(Utils.formatString(Localization['COMMAND_VOTE_VOTED_TEMPLATE'], {
Name: Player.Name,
Prefix: config.commandPrefixes[0],
Action: this.currentVote.actionString,
Reason: args.slice(2).join(' '),
Votes: 1,
minVotes: this.minimumVotes()
}, '%')[0])
this.startVote()
this.voteUpdate()
return
}
this.currentVote.Votes.push(Player)
this.Server.Broadcast(Utils.formatString(Localization['COMMAND_VOTE_VOTED_TEMPLATE'], {
Name: Player.Name,
Prefix: config.commandPrefixes[0],
Action: Utils.formatString(Localization['COMMAND_VOTEKICK_ACTION'], {Name: this.currentVote.Target.Name} , '%')[0],
Votes: this.currentVote.Votes.length,
minVotes: this.minimumVotes()
}, '%')[0])
this.voteUpdate()
}
}
this.Manager.commands['votemap'] = {
ArgumentLength: 1,
Alias: 'vm',
gameTypeExclusions: ['zclassic', 'zstandard', 'infect'],
Permission: Permissions.Commands.COMMAND_USER_CMDS,
inGame: true,
callback: async (Player, args) => {
var Target = await this.Server.getMap(args[1])
switch (true) {
case (Player.Data.lastVote && (new Date() - Player.Data.lastVote) / 1000 < 300):
Player.Tell(Localization['VOTE_COMMANDS_COOLDOWN'])
return
case (!Target):
Player.Tell(Localization['COMMAND_VOTEMAP_NOT_FOUND'])
return
case (Player.cooldownStart && new Date() - Player.cooldownStart > this.cooldownTime):
Player.Tell(Utils.formatString(Localization['COMMAND_VOTE_COOLDOWN'], { Time: parseInt(this.cooldownTime - (new Date() - Player.cooldownStart) / 1000) }, '%')[0])
return
case (this.currentVote.Type && this.currentVote.Type != voteTypes.Map.Name):
case (this.currentVote.Target && this.currentVote.Target.Name != Target.Name):
Player.Tell(Utils.formatString(Localization['COMMAND_VOTE_TYPE_ERR'], { Action: this.currentVote.actionString}, '%')[0])
return
case (this.hasVoted(Player)):
Player.Tell(Localization['COMMAND_VOTE_ALREADY_VOTED'])
return
}
if (!this.currentVote.Origin) {
Player.Data.lastVote = new Date()
this.currentVote = {
Origin: Player,
Target: Target,
Type: voteTypes.Map.Name,
Votes: [Player],
actionString: Utils.formatString(Localization['COMMAND_VOTEMAP_ACTION'], {Name: Target.Alias} , '%')[0],
callback: voteTypes.Map.callback
}
this.Server.Broadcast(Utils.formatString(Localization['COMMAND_VOTE_VOTED_TEMPLATE'], {
Name: Player.Name,
Prefix: config.commandPrefixes[0],
Action: this.currentVote.actionString,
Votes: 1,
minVotes: this.minimumVotes()
}, '%')[0])
this.startVote()
this.voteUpdate()
return
}
this.currentVote.Votes.push(Player)
this.Server.Broadcast(Utils.formatString(Localization['COMMAND_VOTE_VOTED_TEMPLATE'], {
Name: Player.Name,
Prefix: config.commandPrefixes[0],
Action: this.currentVote.actionString,
Votes: this.currentVote.Votes.length,
minVotes: this.minimumVotes()
}, '%')[0])
this.voteUpdate()
}
}
}
}
module.exports = Plugin

View file

@ -0,0 +1,88 @@
const path = require('path')
const fetch = require('node-fetch')
const Utils = new (require(path.join(__dirname, '../Utils/Utils.js')))()
const Localization = require(path.join(__dirname, `../Configuration/Localization-${process.env.LOCALE}.json`)).lookup
const Permissions = require(path.join(__dirname, `../Configuration/NSMConfiguration.json`)).Permissions
class Plugin {
constructor(Server, Manager, Managers) {
this.Server = Server
this.Manager = Manager
this.Managers = Managers
this.joinMessages()
}
async joinMessages() {
this.Server.on('disconnect', async (Player) => {
this.Server.Broadcast(Utils.formatString(Localization['QUIT_PLAYER_BROADCAST'], {Name: Player.Name}, '%')[0])
})
this.Server.on('penalty', async (Type, Target, Reason, Origin, Duration = -1) => {
if (Origin == 1) return
Duration = Duration > 0 ? Utils.time2str(Duration) : ''
this.Server.globalBroadcast(Utils.formatString(Localization[`${Type}_MESSAGE`], {Name: Target.Name, Reason, Origin: Origin.Name, Duration}, '%')[0])
})
this.Server.on('connect', async (Player) => {
if (Player.IPAddress && Player.IPAddress.match(/(unknown|loopback|bot)/g)) return
Player.IPAddress = Player.IPAddress ? Player.IPAddress : (await this.Server.DB.getClient(Player.ClientId)).IPAddress
if (Player.PermissionLevel >= Permissions.Levels['ROLE_MODERATOR']) {
Player.Tell(Utils.formatString(Localization['AUTO_RECENT_REPORTS'], { count: (await this.Server.DB.getActiveReports()).length }, '%')[0])
}
if (process.env.NODE_ENV && process.env.NODE_ENV.toLocaleLowerCase() == 'dev') return
var connections = await this.Server.DB.getAllConnections(Player.ClientId)
Player.Tell(Localization['WELCOME_PLAYER']
.replace('%PLAYER%', Player.Name)
.replace('%CONNECTIONS%', Utils.ordinalSuffix(connections.length | 1)))
if (Player.Session && Player.Session.Data.Authorized) {
Player.Tell('Logged in through previous session')
}
var setting = await this.Server.DB.metaService.getPersistentMeta('location', Player.ClientId)
var role = Utils.getRoleFrom(Player.PermissionLevel, 1).Name
var customTag = await this.Server.DB.metaService.getPersistentMeta('custom_tag', Player.ClientId)
role = customTag ? `^7${customTag.Value}` : role
if (!Player.IPAddress) {
this.Server.Broadcast(Utils.formatString(Localization['WELCOME_PLAYER_BROADCAST'], {
player: Player.Name,
location: Localization['STRING_UNKNOWN'],
level: Player.PermissionLevel,
role
}, '%')[0])
return
}
var info = !(setting && setting.Value == '1')
? await this.getInfo(Player.IPAddress.match(/(localhost|127\.0\.0\.1)/g)
? this.Server.externalIP
: Player.IPAddress)
: { country: Localization['STRING_HIDDEN'] }
this.Server.Broadcast(Utils.formatString(Localization['WELCOME_PLAYER_BROADCAST'], {
player: Player.Name,
location: info ? info.country : Localization['STRING_UNKNOWN'],
level: Player.PermissionLevel,
role
}, '%')[0])
})
}
async getInfo(IPAddress) {
var result = await fetch(`https://extreme-ip-lookup.com/json/${IPAddress.split(':')[0]}?key=demo`)
if (result) {
return await result.json()
}
return false
}
}
module.exports = Plugin

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,187 @@
const path = require('path')
const Utils = new (require(path.join(__dirname, '../Utils/Utils.js')))()
const Localization = require(path.join(__dirname, `../Configuration/Localization-${process.env.LOCALE}.json`)).lookup
const { Command } = require(path.join(__dirname, `../Lib/Classes.js`))
const wait = require('delay')
const Sequelize = require('sequelize')
class Plugin {
constructor(Server, Manager, Managers) {
this.Server = Server
this.Manager = Manager
this.Managers = Managers
this.lockerCost = 100000
this.defaultLockerSize = 1
this.Server.on('preconnect', this.onPlayerConnect.bind(this))
this.Server.on('connect', this.onPlayerConnect.bind(this))
this.Server.on('line', this.onLine.bind(this))
this.Server.on('dvars_loaded', this.init.bind(this))
}
async onLine(line) {
line = line.trim().replace(new RegExp(/([0-9]+:[0-9]+)\s+/g), '')
if (Utils.isJson(line)) {
var lockerEvent = JSON.parse(line)
switch (lockerEvent.event) {
case 'locker_set':
if (!lockerEvent.player) return
var Player = this.Server.Clients.find(c => c && c.Guid == lockerEvent.player.Guid)
if (!Player) return
if (Player.getSelectedLocker == undefined
|| Player.updateLocker == undefined) return
if (!lockerEvent.weapondata) {
Player.getSelectedLocker().weaponData = {}
Player.updateLocker()
return
}
Player.getSelectedLocker().weaponData = lockerEvent.weapondata
Player.updateLocker()
break
}
}
}
async onPlayerConnect(Player) {
var locker = await this.Server.DB.metaService.getPersistentMeta('locker', Player.ClientId)
if (!locker || !locker.Value) {
locker = {}
locker.Value = {
weapons: (new Array(this.defaultLockerSize)).fill({
weaponData: {},
selected: false
})
}
locker.Value.weapons[0].selected = true
locker.Value = JSON.stringify(locker.Value)
await this.Server.DB.metaService.addPersistentMeta('locker', locker.Value, Player.ClientId)
}
Player.locker = JSON.parse(locker.Value)
Player.getSelectedLocker = () => {
return Player.locker.weapons.find(w => w && w.selected)
}
Player.updateLocker = () => {
this.Server.DB.metaService.addPersistentMeta('locker', JSON.stringify(Player.locker), Player.ClientId)
var value = Object.values(Player.getSelectedLocker().weaponData).length
? Object.values(Player.getSelectedLocker().weaponData).toString()
: 'undefined'
this.Server.Rcon.setDvar(`${Player.Guid}_update`, value)
this.Server.Rcon.setDvar(`${Player.Guid}_weapondata`, value)
}
if (!Utils.isJson(locker.Value) || !Player.getSelectedLocker().weaponData) {
this.Server.Rcon.setDvar(`${Player.Guid}_weapondata`, 'undefined')
return
}
this.Server.Rcon.setDvar(`${Player.Guid}_weapondata`, Object.values(Player.getSelectedLocker().weaponData).toString())
}
async addPlayerMoney(ClientId, Money) {
return await this.Server.DB.Models.NSMZombiesStats.update(
{Money : Sequelize.literal(`Money + ${Money}`)},
{where: {ClientId: ClientId}})
}
async getPlayerMoney(ClientId) {
return (await this.Server.DB.Models.NSMZombiesStats.findAll({
where: {
ClientId
},
raw: true
}))[0].Money
}
async init () {
var buyLocker = new Command()
.setName('buylocker')
.addCallback(async (Player) => {
if (!this.Server.DB.Models.NSMZombiesStats) {
return
}
var cost = this.lockerCost * Math.pow(2, Player.locker.weapons.length)
if ((await this.getPlayerMoney(Player.ClientId)) < cost) {
Player.Tell(Localization['ZBANK_BALANCE_ERROR'])
return
}
await this.addPlayerMoney(Player.ClientId, cost * -1)
Player.locker.weapons.push({
weaponData: {},
selected: false
})
Player.updateLocker()
Player.Tell(Utils.formatString(Localization['LOCKER_PURCHASE_SUCCESS'], {cost}, '%')[0])
})
if (this.Server.Gametype == 'zclassic')
this.Manager.Commands.add(buyLocker)
var lockerCmd = new Command()
.setName('locker')
.addParam({
name: 'slot',
optional: true
})
.addCallback(async (Player, params) => {
if (params.slot) {
params.slot = parseInt(params.slot)
if (Player.locker.weapons[params.slot] == undefined) {
Player.Tell(Localization['LOCKER_INVALID_SLOT'])
return
}
for (var i = 0; i < Player.locker.weapons.length; i++) {
Player.locker.weapons[i].selected = false
}
Player.Tell(Player.locker.weapons[params.slot].weaponData.name
? Utils.formatString(Localization['LOCKER_SELECT_SLOT'], {
slot: params.slot,
weaponName: Player.locker.weapons[params.slot].weaponData.name,
clip: Player.locker.weapons[params.slot].weaponData.clip,
stock: Player.locker.weapons[params.slot].weaponData.stock
}, '%')[0]
: Utils.formatString(Localization['LOCKER_SELECT_SLOT_EMPTY'], {slot: params.slot}, '%')[0])
Player.locker.weapons[params.slot].selected = true
Player.updateLocker()
return
}
for (var i = 0; i < Player.locker.weapons.length; i++) {
Player.locker.weapons[i] && Player.locker.weapons[i].weaponData.name
? Player.Tell(Utils.formatString(Localization['COMMAND_LOCKER_FORMAT'], {
weaponName: Player.locker.weapons[i].weaponData.name,
clip: Player.locker.weapons[i].weaponData.clip,
stock: Player.locker.weapons[i].weaponData.stock,
slot: i,
color: Player.locker.weapons[i].selected ? '^2' : '^7'
}, '%')[0])
: Player.Tell(Utils.formatString(Localization['LOCKER_SLOT_EMPTY'], {
color: Player.locker.weapons[i].selected ? '^2' : '^7',
slot: i
}, '%')[0])
await wait(500)
}
Player.Tell(Utils.formatString(Localization['LOCKER_UNK_SLOT'], { cost: this.lockerCost * Math.pow(2, Player.locker.weapons.length) }, '%')[0])
})
if (this.Server.Gametype == 'zclassic')
this.Manager.Commands.add(lockerCmd)
}
}
module.exports = Plugin

View file

@ -0,0 +1,390 @@
const Sequelize = require('sequelize')
const path = require('path')
const Permissions = require(path.join(__dirname, `../Configuration/NSMConfiguration.json`)).Permissions
const Localization = require(path.join(__dirname, `../Configuration/Localization-${process.env.LOCALE}.json`)).lookup
const Utils = new (require(path.join(__dirname, '../Utils/Utils.js')))()
const { emitKeypressEvents } = require('readline')
const { Command } = require(path.join(__dirname, `../Lib/Classes.js`))
class Plugin {
constructor (Server, Manager, Managers) {
//add the .pguid of players to grant them staff permissions (must be done on ClanTag, ZombiesBank, ZombiesStats, NativeCommands & the gsc script staff.gsc)
this.staff_list_a = [564391]
this.Server = Server
this.Manager = Manager
this.Managers = Managers
this.Server.on('connect', this.onPlayerConnect.bind(this))
this.zStats()
// this.getKills()
}
async onPlayerConnect(Player) {
if ((await this.getZStats(Player.ClientId))) return
await this.NSMZStats.build({
ClientId: Player.ClientId
}).save()
}
async createTable() {
this.NSMZStats = this.Server.DB.Models.DB.define('NSMZStats',
{
ClientId: {
type: Sequelize.INTEGER,
autoIncrement: true,
allowNull: false,
primaryKey: true,
references: {
model: 'NSMClients',
key: 'ClientId'
}
},
Kills: {
type: Sequelize.INTEGER,
defaultValue: 0,
allowNull: false
},
Score: {
type: Sequelize.INTEGER,
defaultValue: 0,
allowNull: false
},
Downs: {
type: Sequelize.INTEGER,
defaultValue: 0,
allowNull: false
},
Revives: {
type: Sequelize.INTEGER,
defaultValue: 0,
allowNull: false
},
Headshots: {
type: Sequelize.INTEGER,
defaultValue: 0,
allowNull: false
},
HighestRound: {
type: Sequelize.INTEGER,
defaultValue: 0,
allowNull: false
},
Event: {
type: Sequelize.INTEGER,
defaultValue: 0,
allowNull: false
},
Easter: {
type: Sequelize.INTEGER,
defaultValue: 0,
allowNull: false
},
}, {
timestamps: false
})
this.NSMZStats.sync()
this.Server.DB.Models.NSMZStats = this.NSMZStats
}
async getTopStats(page, limit) {
var Stats = (await this.NSMZStats.findAll({
limit: limit,
offset: page * limit,
attributes: ['ClientId', 'Kills', 'Downs', 'Revives', 'HighestRound', 'Headshots', 'Score', [Sequelize.literal('ROW_NUMBER() over (order by Score desc)'), 'Rank']],
order: [
['Score', 'desc']
]
})).map(x => x = x.dataValues)
for (var i = 0; i < Stats.length; i++) {
Stats[i].Name = await this.Server.DB.getName(Stats[i].ClientId)
}
return Stats
}
async updateStats(Client, Stats, Round = 0) {
Object.entries(Stats).forEach(Stat => {
if (!Client.previousStats) return
if (Stat[1] < Client.previousStats[Stat[0]]) {
Client.previousStats = null
}
})
if (!Client.previousStats) {
Client.previousStats = Stats
return
}
var newStats = {
Kills: Stats.Kills - Client.previousStats.Kills,
Revives: Stats.Revives - Client.previousStats.Revives,
Downs: Stats.Downs - Client.previousStats.Downs,
Score: Stats.Score - Client.previousStats.Score,
Headshots: Stats.Headshots - Client.previousStats.Headshots,
HighestRound: Round
}
Client.previousStats = {...Stats, Round}
this.NSMZStats.update({
Kills: Sequelize.literal(`Kills + ${newStats.Kills}`),
Downs: Sequelize.literal(`Downs + ${newStats.Downs}`),
Revives: Sequelize.literal(`Revives + ${newStats.Revives}`),
Score: Sequelize.literal(`Score + ${newStats.Score}`),
Headshots: Sequelize.literal(`Headshots + ${newStats.Headshots}`)
},
{where: {ClientId: Client.ClientId}})
if (Round) {
this.NSMZStats.update({
HighestRound: Round,
}, { where: {
ClientId: Client.ClientId,
HighestRound: {
[Sequelize.Op.lt]: Round
}
}})
}
}
async getKills(client)
{
//var ClientId = client.ClientId
var Stats = (await this.NSMZStats.findAll({where: client.ClientId})).map(x => x = x.dataValues)
console.log(client.Name + " " + Stats.Kills);
}
async getZStats(ClientId) {
var Stats = (await this.NSMZStats.findAll({where: ClientId})).map(x => x = x.dataValues)
return Stats.length > 0 ? Stats[0] : false
}
async print_servers(manager, Player, delay)
{
await new Promise(resolve => setTimeout(resolve, delay * 500))
if (manager && manager.Server.Hostname)
{
if(!(manager.Server.Hostname.includes('PRIVATE')))
{
Player.Tell(`^2.king^7 ^2${manager.Server.Hostname.split(" ")[2]} ^3`)
}
}
}
async king_lock_check(manager, Player)
{
var guild_quest = await this.Server.DB.metaService.getPersistentMeta('guild_quest', 12)
if(guild_quest.Value.split(";")[1] == "gamemode_speedrun_quest_pia" && (manager.Server.Hostname.includes('PANZER'))
|| guild_quest.Value.split(";")[1] == "gamemode_speedrun_quest_titb" && (manager.Server.Hostname.includes('BUS'))
|| guild_quest.Value.split(";")[1] == "gamemode_speedrun_quest_botb" && (manager.Server.Hostname.includes('BRUTUS'))
|| guild_quest.Value.split(";")[1] == "ee_speedrun_quest_transit" && (manager.Server.Hostname.includes('TRANZIT2'))
|| guild_quest.Value.split(";")[1] == "ee_speedrun_quest_highrise" && (manager.Server.Hostname.includes('DIE RISE'))
|| guild_quest.Value.split(";")[1] == "ee_speedrun_quest_prison" && (manager.Server.Hostname.includes('MOTD'))
|| guild_quest.Value.split(";")[1] == "ee_speedrun_quest_buried" && (manager.Server.Hostname.includes('BURIED'))
|| guild_quest.Value.split(";")[1] == "ee_speedrun_quest_tomb" && (manager.Server.Hostname.includes('ORIGINS'))
|| manager.Server.Hostname.includes('RAID'))
{
Player.Tell("Cannot king a ^1Competitive Server^7.")
return
}
if (await manager.Server.Rcon.getDvar(`king_lock`) == "1")
{
Player.Tell("This server is ^1too advanced^7 to ^3.king^7, please wait a few minutes.")
await manager.Server.Rcon.setDvar('ln', "^5King^7 request ^1denied^7. Too far into the game.")
return
}
if (await manager.Server.Rcon.getDvar(`is_first_room`) == "1")
{
Player.Tell("Cannot .king into a first room challenge.")
await manager.Server.Rcon.setDvar('ln', "^5King^7 request ^1denied^7. First room protection")
return;
}
Player.Data.lastKing = new Date()
var king2 = await this.Server.DB.metaService.getPersistentMeta('king2', Player.ClientId)
var king3 = await this.Server.DB.metaService.getPersistentMeta('king3', Player.ClientId)
var king4 = await this.Server.DB.metaService.getPersistentMeta('king4', Player.ClientId)
if (king4)
await manager.Server.Rcon.executeCommandAsync(`set king ${Player.Name};${Player.ClientId};4`)
else if (king3)
await manager.Server.Rcon.executeCommandAsync(`set king ${Player.Name};${Player.ClientId};3`)
else if (king2)
await manager.Server.Rcon.executeCommandAsync(`set king ${Player.Name};${Player.ClientId};2`)
else
await manager.Server.Rcon.executeCommandAsync(`set king ${Player.Name};${Player.ClientId};1`)
console.log("king dvar set")
}
async is_staff(Player)
{
for (var i = 0; i < this.staff_list_a.length; i++)
if (this.staff_list_a[i] == Player.Guid)
return true
Player.Tell("hehe boi u tryna scam da kiels by using staff cmd?")
return false
}
async zStats() {
this.Manager.on('webfront-ready', (Webfront) => {
Webfront.addHeaderHtml(`<a href='/zstats' class='wf-header-link'><i class="fas fa-skull"></i></a>`, 3)
})
await this.createTable()
this.Manager.commands['king'] = {
ArgumentLength: 0,
inGame: false,
logToAudit: false,
Permission: Permissions.Commands.COMMAND_USER_CMDS,
callback: async (Player, args) => {
if (!args[1])
{
Player.Tell("^3Usage^7 : ")
await new Promise(resolve => setTimeout(resolve, 300))
var i = 0;
this.Managers.forEach(manager =>
{
this.print_servers(manager, Player, i);
i++
})
return
}
var king = await this.Server.DB.metaService.getPersistentMeta('king', Player.ClientId)
var isServerFound = false;
var king2 = await this.Server.DB.metaService.getPersistentMeta('king2', Player.ClientId)
var king3 = await this.Server.DB.metaService.getPersistentMeta('king3', Player.ClientId)
var king4 = await this.Server.DB.metaService.getPersistentMeta('king4', Player.ClientId)
if (king4 && Player.Data && Player.Data.lastKing && (new Date() - Player.Data.lastKing) / 1000 < 60) {
Player.Tell("wait up to 1 min to use .king again")
return
}
else if (king3 && Player.Data && Player.Data.lastKing && (new Date() - Player.Data.lastKing) / 1000 < 180) {
Player.Tell("wait up to 3 mins to use .king again")
return
}
else if (king2 && Player.Data && Player.Data.lastKing && (new Date() - Player.Data.lastKing) / 1000 < 300) {
Player.Tell("wait up to 5 mins to use .king again")
return
}
else if (king && Player.Data && Player.Data.lastKing && (new Date() - Player.Data.lastKing) / 1000 < 900) {
Player.Tell("wait up to 15 mins to use .king again")
return
}
if (king || king2 || king3 || king4 || await this.is_staff(Player) == true)
{
this.Managers.forEach(manager =>
{
if (manager && manager.Server.Hostname)
{
if((manager.Server.Hostname.includes('ORIGIN'))) //event
{
Player.Tell("^3.king ^1temp disabled^7 on origins due to ^3ongoing event")
return
}
if(!(manager.Server.Hostname.includes('PRIVATE')))
{
var serverName = ""
if (manager.Server.Hostname.split(" ")[2])
{
var serverName = manager.Server.Hostname.split(" ")[2].toLocaleLowerCase()
}
if (serverName == args[1].toLocaleLowerCase())
{
isServerFound = true
if (!king2 && !king3 && !king4 && serverName == this.Manager.Server.Hostname.split(" ")[2].toLocaleLowerCase())
{
Player.Tell("You are ^3already connected^7 to this ^3server.")
return
}
if (manager.Server.getClients().length != manager.Server.MaxClients)
{
Player.Tell(`^3${args[1]} is not full`)
return
}
this.king_lock_check(manager, Player);
return
}
}
}
})
if (isServerFound == false)
{
Player.Tell("^3Server not found. use ^2.king ^3to see the ^5list of servers")
return
}
}
else
{
Player.Tell("^1King^7 command only. Earn it in events")
}
}
}
this.Manager.commands['zstats'] = {
ArgumentLength: 0,
inGame: false,
logToAudit: false,
Permission: Permissions.Commands.COMMAND_USER_CMDS,
callback: async (Player, args) => {
var Client = args[1] ? await this.Server.getClient(args[1]) : Player
if (!Client) {
Player.Tell(Localization['COMMAND_CLIENT_NOT_FOUND'])
return
}
var Stats = await this.getZStats(Client.ClientId)
if (!Stats) {
Player.Tell(Localization['STATS_NOT_EXIST'])
return
}
Stats.Player = Client.Name
var formattedStats = Utils.formatString(Localization['COMMAND_ZSTATS_FORMAT'], Stats, '%');
formattedStats.forEach(async line => {
await Player.Tell(line)
})
}
}
this.Server.on('connect', async (Client) => {
Client.previousStats = null
Client.on('round_start', async (Round, Stats) => {
await this.updateStats(Client, Stats, Round)
})
Client.on('update_stats', async (Round, Stats) => {
await this.updateStats(Client, Stats, Round)
})
})
this.Server.on('line', async (data) => {
data = data.trim().replace(new RegExp(/([0-9]+:[0-9]+)\s+/g), '')
if (Utils.isJson(data) && JSON.parse(data).event) {
var event = JSON.parse(data)
switch (event.event) {
case 'round_start':
this.Server.emit('round_start', event.round)
this.roundNumber = event.round
event.players.forEach(Player => {
this.Server.Clients.forEach(async Client => {
if (!Client) return
if (Client.Guid == Player.Guid) {
Client.emit('round_start', event.round, Player.Stats)
}
})
})
break
case 'player_downed':
case 'player_revived':
case 'update_stats':
this.Server.Clients.forEach(async Client => {
if (!Client) return
if (Client.Guid == event.player.Guid) {
Client.emit('update_stats', null, event.player.Stats)
}
})
break
}
}
})
}
}
module.exports = Plugin

Binary file not shown.

Binary file not shown.

Binary file not shown.

93
t6/dedicated_zmbrutus.cfg Normal file
View file

@ -0,0 +1,93 @@
//////////////////////////////////////////////////
/// PlutoT6 ZM ServerConfiguration file //
///////////////////////////////////////////////////
// This config best view with Notepad++ OR //
// other *nix compatible editors of your choice. //
///////////////////////////////////////////////////
// Remove "//" in front of lines to allow the //
// server to read them. //
// Anything after "//" is a comment. //
//////////////////////////////////////////////////
// GENERAL SETTINGS //
//////////////////////////////////////////////////
//g_password "Flawless" // Require a password to join your server. (Use "password <yourpassword>" to set it on the client before connecting)
sv_maxclients 8 // Maximum players that are allowed in your server. (1-8, default is 4) Keeping this at 1-4 is recommended to prevent game breaking bugs.
//zombies_minplayers 2 // Minimum players needed to start the game (1-8, default is 1). Do NOT set this higher than sv_maxclients!
//sv_minPing 0 // Minimum ping needed to the server? (NOT recommended to edit, terribly broken and inaccurate since ages!)
//sv_maxPing 400 // Maximum ping allowed to the server? (NOT recommended to edit, terribly broken and inaccurate since ages!)
//zm_gungame 1 // Enable Pluto's custom Gun Game?
//zm_sharpshooter 1 // Enable Pluto's custom Sharp Shooter?
//gts zmDifficulty 1 // Difficulty? 0 = Easy, 1 = Normal !!If set to easy it must be manually set on the client as well!!
demo_enabled 0 // Record matches as demo files? 1 = Enabled, 0 = Disabled (Very efficient <5MB per match for a full server)
sv_sayname "" // name server-side 'say' commands show up as
sv_voice "1" // Allow Voice Chat (0 = Disable 1 = Everyone hears you. 2 = Teams only)
sv_voicequality "9" // Voice Chat Quality. (0-9) Default is 3 (= Steam/Console quality). Use 9 for the best quality.
sv_allowAimAssist 1 // Allow Aim Assist on gamepads. (0 = Will lock the option on gamepad controls menu.)
sv_fix_zm_weapons true // Fix the SMR's ADS spread, 870 MCS's penetration damage and allow sprinting with Galvaknuckles
sv_patch_zm_weapons true // Apply Post DLC1 changes to tar21_zm, type95_zm, xm8_zm, an94_zm, hamr_zm, rpd_zm, pdw57_zm, kard_zm ? (only recoil changes)
//////////////////////////////////////////////////
//These options can also be configured individually on a map basis in each zm config in gamesettings.
//////////////////////////////////////////////////
//gts startRound 1 // Starting Round. Works on all maps.
//gts magic 1 // Remove all supernatural assistance? Only Survival and Grief have this option!
//gts headshotsonly 0 // Headshots only? Only Survival and Grief have this option!
//gts allowdogs 1 // Allow Hellhounds? Only Survival has this option!
//gts cleansedLoadout 1 // Allow players to choose their Loadout? Only Turned has this option!
//gts teamCount 2 // Sets the number of teams 2. Set to 2 by default when loading grief.
//////////////////////////////////////////////////
// B3, IW4MADMIN, GAME LOG & RCON SETTINGS //
//////////////////////////////////////////////////
g_logSync 2 // 0 only flush on game end, 1 flush when buffer full, 2 always flush after a write, 3 append to old logs. (Keep this at 2 if you plan on using a 3rd party admin tool)
g_log "logs\games_zmbrutus.log" // If you choose to use this make sure the filename is unique for each server!
rcon_password "rconAQW25wqa" // RemoteCONtrol password. !!Do NOT skip if you plan on using a 3rd party admin tool such as B3 or IW4m-Admin!!
rcon_rate_limit "0" // Rate limit RCon; limit is per IP; range is 0 to 10 000; value is in milliseconds. Lower this if you use IW4mA's Game Interface.
rconWhitelistAdd "127.0.0.1" // Command used to add an IP address to the whitelist. When no IP is added all IPs can send rcon commands.
rconWhitelistAdd "198.251.84.64" // If it is set only the whitelisted IPs and loopback (127.0.0.0/8) can send commands.
seta sv_wwwBaseURL "" // Configure the URL to Fast DL mods from. (i.e. http://domain.tld/iw5)
seta fs_game "" // What mod are we loading? (i.e. "mods/MyMod")
//////////////////////////////////////////////////
//The "exec zm_<gametype>_<location>.cfg" line sets the dvars for the location and gametype for you. This .cfg applies to all following maps in the rotation like MP until another .cfg is defined.
//You may modify these .cfgs in gamesettings. Make sure only one sv_maprotation line is uncommented or you may run into errors on starting or joining the server.
//Currently rotating the map using sv_maprotation doesn't work properly, i.e. clients will be kicked with an error when the map rotates to another map.
//It's recommended to only include one map in your sv_maprotation for this reason.
//////////////////////////////////////////////////////////////////////////////
// Maps and the matching configs //
//////////////////////////////////////////////////////////////////////////////
// Buried - exec zm_classic_processing.cfg map zm_buried //
// Buried Turned - exec zm_cleansed_street.cfg map zm_buried //
// Buried Grief - exec zm_grief_street.cfg map zm_buried //
// Die Rise - exec zm_classic_rooftop.cfg map zm_highrise //
// Mob of The Dead - exec zm_classic_prison.cfg map zm_prison //
// Mob of The Dead Grief - exec zm_grief_cellblock.cfg map zm_prison //
// Nuketown - exec zm_standard_nuked.cfg map zm_nuked //
// Origins - exec zm_classic_tomb.cfg map zm_tomb //
// Tranzit - exec zm_classic_transit.cfg map zm_transit //
// Tranzit Farm Survival - exec zm_standard_farm.cfg map zm_transit //
// Tranzit Town Survival - exec zm_standard_town.cfg map zm_transit //
// Tranzit Bus Depot Survival - exec zm_standard_transit.cfg map zm_transit //
// Tranzit Farm Grief - exec zm_grief_farm.cfg map zm_transit //
// Tranzit Town Grief - exec zm_grief_town.cfg map zm_transit //
// Tranzit Bus Depot Grief - exec zm_grief_transit.cfg map zm_transit //
// Tranzit Diner Turned - exec zm_cleansed_diner.cfg map zm_transit_dr //
//////////////////////////////////////////////////////////////////////////////
// Notes:
// Town/Tranzit servers will not natively spawn in tombstone since servers launch the maps in solo.
// --> https://forum.plutonium.pw/topic/124
// Grief requires a fix otherwise teams won't balance resulting in clients crashing when a 5th player joins.
// --> https://forum.plutonium.pw/topic/4057
//Classic/TranZit Maps rotation
sv_maprotation "exec zm_classic_prison.cfg map zm_prison"
//Survival Maps rotation
//sv_maprotation "exec zm_standard_town.cfg map zm_transit"
//Grief Maps rotation
//sv_maprotation "exec zm_grief_town.cfg map zm_transit exec zm_grief_transit.cfg map zm_transit exec zm_grief_farm.cfg map zm_transit exec zm_grief_cellblock.cfg map zm_prison exec zm_grief_street.cfg map zm_buried"
//Turned Maps rotation
//sv_maprotation "exec zm_cleansed_diner.cfg map zm_transit_dr exec zm_cleansed_street.cfg map zm_buried"
//Congratulations. You reached the end of this file. Leave map_rotate down below or else the server will not start after launch...
map_rotate

94
t6/dedicated_zmburied.cfg Normal file
View file

@ -0,0 +1,94 @@
//////////////////////////////////////////////////
/// PlutoT6 ZM ServerConfiguration file //
///////////////////////////////////////////////////
// This config best view with Notepad++ OR //
// other *nix compatible editors of your choice. //
///////////////////////////////////////////////////
// Remove "//" in front of lines to allow the //
// server to read them. //
// Anything after "//" is a comment. //
//////////////////////////////////////////////////
// GENERAL SETTINGS //
//////////////////////////////////////////////////
//g_password "aqw" // Require a password to join your server. (Use "password <yourpassword>" to set it on the client before connecting)
sv_maxclients 8 // Maximum players that are allowed in your server. (1-8, default is 4) Keeping this at 1-4 is recommended to prevent game breaking bugs.
//zombies_minplayers 1 // Minimum players needed to start the game (1-8, default is 1). Do NOT set this higher than sv_maxclients!
//sv_minPing 0 // Minimum ping needed to the server? (NOT recommended to edit, terribly broken and inaccurate since ages!)
//sv_maxPing 400 // Maximum ping allowed to the server? (NOT recommended to edit, terribly broken and inaccurate since ages!)
//zm_gungame 1 // Enable Pluto's custom Gun Game?
//zm_sharpshooter 1 // Enable Pluto's custom Sharp Shooter?
//gts zmDifficulty 1 // Difficulty? 0 = Easy, 1 = Normal !!If set to easy it must be manually set on the client as well!!
demo_enabled 0 // Record matches as demo files? 1 = Enabled, 0 = Disabled (Very efficient <5MB per match for a full server)
sv_sayname "" // name server-side 'say' commands show up as
sv_voice "1" // Allow Voice Chat (0 = Disable 1 = Everyone hears you. 2 = Teams only)
sv_voicequality "9" // Voice Chat Quality. (0-9) Default is 3 (= Steam/Console quality). Use 9 for the best quality.
sv_allowAimAssist 1 // Allow Aim Assist on gamepads. (0 = Will lock the option on gamepad controls menu.)
sv_fix_zm_weapons true // Fix the SMR's ADS spread, 870 MCS's penetration damage and allow sprinting with Galvaknuckles
sv_patch_zm_weapons true // Apply Post DLC1 changes to tar21_zm, type95_zm, xm8_zm, an94_zm, hamr_zm, rpd_zm, pdw57_zm, kard_zm ? (only recoil changes)
//////////////////////////////////////////////////
//These options can also be configured individually on a map basis in each zm config in gamesettings.
//////////////////////////////////////////////////
//gts startRound 29 // Starting Round. Works on all maps.
//gts magic 1 // Remove all supernatural assistance? Only Survival and Grief have this option!
//gts headshotsonly 0 // Headshots only? Only Survival and Grief have this option!
//gts allowdogs 1 // Allow Hellhounds? Only Survival has this option!
//gts cleansedLoadout 1 // Allow players to choose their Loadout? Only Turned has this option!
//gts teamCount 2 // Sets the number of teams 2. Set to 2 by default when loading grief.
//////////////////////////////////////////////////
// B3, IW4MADMIN, GAME LOG & RCON SETTINGS //
//////////////////////////////////////////////////
g_logSync 2 // 0 only flush on game end, 1 flush when buffer full, 2 always flush after a write, 3 append to old logs. (Keep this at 2 if you plan on using a 3rd party admin tool)
g_log "logs\games_zmburied.log" // If you choose to use this make sure the filename is unique for each server!
rcon_password "rconAQW25wqa" // RemoteCONtrol password. !!Do NOT skip if you plan on using a 3rd party admin tool such as B3 or IW4m-Admin!!
rcon_rate_limit "0" // Rate limit RCon; limit is per IP; range is 0 to 10 000; value is in milliseconds. Lower this if you use IW4mA's Game Interface.
rconWhitelistAdd "127.0.0.1" // Command used to add an IP address to the whitelist. When no IP is added all IPs can send rcon commands.
rconWhitelistAdd "198.251.84.64" // If it is set only the whitelisted IPs and loopback (127.0.0.0/8) can send commands.
seta sv_wwwBaseURL "" // Configure the URL to Fast DL mods from. (i.e. http://domain.tld/iw5)
seta fs_game "" // What mod are we loading? (i.e. "mods/MyMod")
//////////////////////////////////////////////////
//The "exec zm_<gametype>_<location>.cfg" line sets the dvars for the location and gametype for you. This .cfg applies to all following maps in the rotation like MP until another .cfg is defined.
//You may modify these .cfgs in gamesettings. Make sure only one sv_maprotation line is uncommented or you may run into errors on starting or joining the server.
//Currently rotating the map using sv_maprotation doesn't work properly, i.e. clients will be kicked with an error when the map rotates to another map.
//It's recommended to only include one map in your sv_maprotation for this reason.
//////////////////////////////////////////////////////////////////////////////
// Maps and the matching configs //
//////////////////////////////////////////////////////////////////////////////
// Buried - exec zm_classic_processing.cfg map zm_buried //
// Buried Turned - exec zm_cleansed_street.cfg map zm_buried //
// Buried Grief - exec zm_grief_street.cfg map zm_buried //
// Die Rise - exec zm_classic_rooftop.cfg map zm_highrise //
// Mob of The Dead - exec zm_classic_prison.cfg map zm_prison //
// Mob of The Dead Grief - exec zm_grief_cellblock.cfg map zm_prison //
// Nuketown - exec zm_standard_nuked.cfg map zm_nuked //
// Origins - exec zm_classic_tomb.cfg map zm_tomb //
// Tranzit - exec zm_classic_transit.cfg map zm_transit //
// Tranzit Farm Survival - exec zm_standard_farm.cfg map zm_transit //
// Tranzit Town Survival - exec zm_standard_town.cfg map zm_transit //
// Tranzit Bus Depot Survival - exec zm_standard_transit.cfg map zm_transit //
// Tranzit Farm Grief - exec zm_grief_farm.cfg map zm_transit //
// Tranzit Town Grief - exec zm_grief_town.cfg map zm_transit //
// Tranzit Bus Depot Grief - exec zm_grief_transit.cfg map zm_transit //
// Tranzit Diner Turned - exec zm_cleansed_diner.cfg map zm_transit_dr //
//////////////////////////////////////////////////////////////////////////////
// Notes:
// Town/Tranzit servers will not natively spawn in tombstone since servers launch the maps in solo.
// --> https://forum.plutonium.pw/topic/124
// Grief requires a fix otherwise teams won't balance resulting in clients crashing when a 5th player joins.
// --> https://forum.plutonium.pw/topic/4057
//Classic/TranZit Maps rotation
sv_maprotation "exec zm_classic_processing.cfg map zm_buried"
//Survival Maps rotation
//sv_maprotation "exec zm_standard_town.cfg map zm_transit exec zm_standard_transit.cfg map zm_transit exec zm_standard_farm.cfg map zm_transit exec zm_standard_nuked.cfg map zm_nuked"
//Grief Maps rotation
//sv_maprotation "exec zm_grief_town.cfg map zm_transit exec zm_grief_transit.cfg map zm_transit exec zm_grief_farm.cfg map zm_transit exec zm_grief_cellblock.cfg map zm_prison exec zm_grief_street.cfg map zm_buried"
//Turned Maps rotation
//sv_maprotation "exec zm_cleansed_diner.cfg map zm_transit_dr exec zm_cleansed_street.cfg map zm_buried"
//Congratulations. You reached the end of this file. Leave map_rotate down below or else the server will not start after launch...
map_rotate

View file

@ -0,0 +1,93 @@
//////////////////////////////////////////////////
/// PlutoT6 ZM ServerConfiguration file //
///////////////////////////////////////////////////
// This config best view with Notepad++ OR //
// other *nix compatible editors of your choice. //
///////////////////////////////////////////////////
// Remove "//" in front of lines to allow the //
// server to read them. //
// Anything after "//" is a comment. //
//////////////////////////////////////////////////
// GENERAL SETTINGS //
//////////////////////////////////////////////////
//g_password "aqw" // Require a password to join your server. (Use "password <yourpassword>" to set it on the client before connecting)
sv_maxclients 8 // Maximum players that are allowed in your server. (1-8, default is 4) Keeping this at 1-4 is recommended to prevent game breaking bugs.
//zombies_minplayers 1 // Minimum players needed to start the game (1-8, default is 1). Do NOT set this higher than sv_maxclients!
//sv_minPing 0 // Minimum ping needed to the server? (NOT recommended to edit, terribly broken and inaccurate since ages!)
//sv_maxPing 400 // Maximum ping allowed to the server? (NOT recommended to edit, terribly broken and inaccurate since ages!)
//zm_gungame 1 // Enable Pluto's custom Gun Game?
//zm_sharpshooter 1 // Enable Pluto's custom Sharp Shooter?
//gts zmDifficulty 1 // Difficulty? 0 = Easy, 1 = Normal !!If set to easy it must be manually set on the client as well!!
demo_enabled 0 // Record matches as demo files? 1 = Enabled, 0 = Disabled (Very efficient <5MB per match for a full server)
sv_sayname "" // name server-side 'say' commands show up as
sv_voice "1" // Allow Voice Chat (0 = Disable 1 = Everyone hears you. 2 = Teams only)
sv_voicequality "9" // Voice Chat Quality. (0-9) Default is 3 (= Steam/Console quality). Use 9 for the best quality.
sv_allowAimAssist 1 // Allow Aim Assist on gamepads. (0 = Will lock the option on gamepad controls menu.)
sv_fix_zm_weapons true // Fix the SMR's ADS spread, 870 MCS's penetration damage and allow sprinting with Galvaknuckles
sv_patch_zm_weapons true // Apply Post DLC1 changes to tar21_zm, type95_zm, xm8_zm, an94_zm, hamr_zm, rpd_zm, pdw57_zm, kard_zm ? (only recoil changes)
//////////////////////////////////////////////////
//These options can also be configured individually on a map basis in each zm config in gamesettings.
//////////////////////////////////////////////////
//gts startRound 1 // Starting Round. Works on all maps.
//gts magic 1 // Remove all supernatural assistance? Only Survival and Grief have this option!
//gts headshotsonly 0 // Headshots only? Only Survival and Grief have this option!
//gts allowdogs 1 // Allow Hellhounds? Only Survival has this option!
//gts cleansedLoadout 1 // Allow players to choose their Loadout? Only Turned has this option!
//gts teamCount 2 // Sets the number of teams 2. Set to 2 by default when loading grief.
//////////////////////////////////////////////////
// B3, IW4MADMIN, GAME LOG & RCON SETTINGS //
//////////////////////////////////////////////////
g_logSync 2 // 0 only flush on game end, 1 flush when buffer full, 2 always flush after a write, 3 append to old logs. (Keep this at 2 if you plan on using a 3rd party admin tool)
g_log "logs\games_zmdierise.log" // If you choose to use this make sure the filename is unique for each server!
rcon_password "rconAQW25wqa" // RemoteCONtrol password. !!Do NOT skip if you plan on using a 3rd party admin tool such as B3 or IW4m-Admin!!
rcon_rate_limit "0" // Rate limit RCon; limit is per IP; range is 0 to 10 000; value is in milliseconds. Lower this if you use IW4mA's Game Interface.
rconWhitelistAdd "127.0.0.1" // Command used to add an IP address to the whitelist. When no IP is added all IPs can send rcon commands.
rconWhitelistAdd "198.251.84.64" // If it is set only the whitelisted IPs and loopback (127.0.0.0/8) can send commands.
//seta sv_wwwBaseURL "http://198.251.88.2/" // Configure the URL to Fast DL mods from. (i.e. http://domain.tld/iw5)
s//eta fs_game "mods/mymods" // What mod are we loading? (i.e. "mods/MyMod")
//////////////////////////////////////////////////
//The "exec zm_<gametype>_<location>.cfg" line sets the dvars for the location and gametype for you. This .cfg applies to all following maps in the rotation like MP until another .cfg is defined.
//You may modify these .cfgs in gamesettings. Make sure only one sv_maprotation line is uncommented or you may run into errors on starting or joining the server.
//Currently rotating the map using sv_maprotation doesn't work properly, i.e. clients will be kicked with an error when the map rotates to another map.
//It's recommended to only include one map in your sv_maprotation for this reason.
//////////////////////////////////////////////////////////////////////////////
// Maps and the matching configs //
//////////////////////////////////////////////////////////////////////////////
// Buried - exec zm_classic_processing.cfg map zm_buried //
// Buried Turned - exec zm_cleansed_street.cfg map zm_buried //
// Buried Grief - exec zm_grief_street.cfg map zm_buried //
// Die Rise - exec zm_classic_rooftop.cfg map zm_highrise //
// Mob of The Dead - exec zm_classic_prison.cfg map zm_prison //
// Mob of The Dead Grief - exec zm_grief_cellblock.cfg map zm_prison //
// Nuketown - exec zm_standard_nuked.cfg map zm_nuked //
// Origins - exec zm_classic_tomb.cfg map zm_tomb //
// Tranzit - exec zm_classic_transit.cfg map zm_transit //
// Tranzit Farm Survival - exec zm_standard_farm.cfg map zm_transit //
// Tranzit Town Survival - exec zm_standard_town.cfg map zm_transit //
// Tranzit Bus Depot Survival - exec zm_standard_transit.cfg map zm_transit //
// Tranzit Farm Grief - exec zm_grief_farm.cfg map zm_transit //
// Tranzit Town Grief - exec zm_grief_town.cfg map zm_transit //
// Tranzit Bus Depot Grief - exec zm_grief_transit.cfg map zm_transit //
// Tranzit Diner Turned - exec zm_cleansed_diner.cfg map zm_transit_dr //
//////////////////////////////////////////////////////////////////////////////
// Notes:
// Town/Tranzit servers will not natively spawn in tombstone since servers launch the maps in solo.
// --> https://forum.plutonium.pw/topic/124
// Grief requires a fix otherwise teams won't balance resulting in clients crashing when a 5th player joins.
// --> https://forum.plutonium.pw/topic/4057
//Classic/TranZit Maps rotation
sv_maprotation "exec zm_classic_rooftop.cfg map zm_highrise"
//Survival Maps rotation
//sv_maprotation "exec zm_standard_town.cfg map zm_transit"
//Grief Maps rotation
//sv_maprotation "exec zm_grief_town.cfg map zm_transit exec zm_grief_transit.cfg map zm_transit exec zm_grief_farm.cfg map zm_transit exec zm_grief_cellblock.cfg map zm_prison exec zm_grief_street.cfg map zm_buried"
//Turned Maps rotation
//sv_maprotation "exec zm_cleansed_diner.cfg map zm_transit_dr exec zm_cleansed_street.cfg map zm_buried"
//Congratulations. You reached the end of this file. Leave map_rotate down below or else the server will not start after launch...
map_rotate

93
t6/dedicated_zmmotd.cfg Normal file
View file

@ -0,0 +1,93 @@
//////////////////////////////////////////////////
/// PlutoT6 ZM ServerConfiguration file //
///////////////////////////////////////////////////
// This config best view with Notepad++ OR //
// other *nix compatible editors of your choice. //
///////////////////////////////////////////////////
// Remove "//" in front of lines to allow the //
// server to read them. //
// Anything after "//" is a comment. //
//////////////////////////////////////////////////
// GENERAL SETTINGS //
//////////////////////////////////////////////////
//g_password "" // Require a password to join your server. (Use "password <yourpassword>" to set it on the client before connecting)
sv_maxclients 8 // Maximum players that are allowed in your server. (1-8, default is 4) Keeping this at 1-4 is recommended to prevent game breaking bugs.
//zombies_minplayers 3 // Minimum players needed to start the game (1-8, default is 1). Do NOT set this higher than sv_maxclients!
//sv_minPing 0 // Minimum ping needed to the server? (NOT recommended to edit, terribly broken and inaccurate since ages!)
//sv_maxPing 400 // Maximum ping allowed to the server? (NOT recommended to edit, terribly broken and inaccurate since ages!)
//zm_gungame 1 // Enable Pluto's custom Gun Game?
//zm_sharpshooter 1 // Enable Pluto's custom Sharp Shooter?
//gts zmDifficulty 1 // Difficulty? 0 = Easy, 1 = Normal !!If set to easy it must be manually set on the client as well!!
demo_enabled 0 // Record matches as demo files? 1 = Enabled, 0 = Disabled (Very efficient <5MB per match for a full server)
sv_sayname "" // name server-side 'say' commands show up as
sv_voice "1" // Allow Voice Chat (0 = Disable 1 = Everyone hears you. 2 = Teams only)
sv_voicequality "9" // Voice Chat Quality. (0-9) Default is 3 (= Steam/Console quality). Use 9 for the best quality.
sv_allowAimAssist 1 // Allow Aim Assist on gamepads. (0 = Will lock the option on gamepad controls menu.)
sv_fix_zm_weapons true // Fix the SMR's ADS spread, 870 MCS's penetration damage and allow sprinting with Galvaknuckles
sv_patch_zm_weapons true // Apply Post DLC1 changes to tar21_zm, type95_zm, xm8_zm, an94_zm, hamr_zm, rpd_zm, pdw57_zm, kard_zm ? (only recoil changes)
//////////////////////////////////////////////////
//These options can also be configured individually on a map basis in each zm config in gamesettings.
//////////////////////////////////////////////////
//gts startRound 1 // Starting Round. Works on all maps.
//gts magic 1 // Remove all supernatural assistance? Only Survival and Grief have this option!
//gts headshotsonly 0 // Headshots only? Only Survival and Grief have this option!
//gts allowdogs 1 // Allow Hellhounds? Only Survival has this option!
//gts cleansedLoadout 1 // Allow players to choose their Loadout? Only Turned has this option!
//gts teamCount 2 // Sets the number of teams 2. Set to 2 by default when loading grief.
//////////////////////////////////////////////////
// B3, IW4MADMIN, GAME LOG & RCON SETTINGS //
//////////////////////////////////////////////////
g_logSync 2 // 0 only flush on game end, 1 flush when buffer full, 2 always flush after a write, 3 append to old logs. (Keep this at 2 if you plan on using a 3rd party admin tool)
g_log "logs\games_zmmotd.log" // If you choose to use this make sure the filename is unique for each server!
rcon_password "rconAQW25wqa" // RemoteCONtrol password. !!Do NOT skip if you plan on using a 3rd party admin tool such as B3 or IW4m-Admin!!
rcon_rate_limit "0" // Rate limit RCon; limit is per IP; range is 0 to 10 000; value is in milliseconds. Lower this if you use IW4mA's Game Interface.
rconWhitelistAdd "127.0.0.1" // Command used to add an IP address to the whitelist. When no IP is added all IPs can send rcon commands.
rconWhitelistAdd "198.251.84.64" // If it is set only the whitelisted IPs and loopback (127.0.0.0/8) can send commands.
seta sv_wwwBaseURL "" // Configure the URL to Fast DL mods from. (i.e. http://domain.tld/iw5)
seta fs_game "" // What mod are we loading? (i.e. "mods/MyMod")
//////////////////////////////////////////////////
//The "exec zm_<gametype>_<location>.cfg" line sets the dvars for the location and gametype for you. This .cfg applies to all following maps in the rotation like MP until another .cfg is defined.
//You may modify these .cfgs in gamesettings. Make sure only one sv_maprotation line is uncommented or you may run into errors on starting or joining the server.
//Currently rotating the map using sv_maprotation doesn't work properly, i.e. clients will be kicked with an error when the map rotates to another map.
//It's recommended to only include one map in your sv_maprotation for this reason.
//////////////////////////////////////////////////////////////////////////////
// Maps and the matching configs //
//////////////////////////////////////////////////////////////////////////////
// Buried - exec zm_classic_processing.cfg map zm_buried //
// Buried Turned - exec zm_cleansed_street.cfg map zm_buried //
// Buried Grief - exec zm_grief_street.cfg map zm_buried //
// Die Rise - exec zm_classic_rooftop.cfg map zm_highrise //
// Mob of The Dead - exec zm_classic_prison.cfg map zm_prison //
// Mob of The Dead Grief - exec zm_grief_cellblock.cfg map zm_prison //
// Nuketown - exec zm_standard_nuked.cfg map zm_nuked //
// Origins - exec zm_classic_tomb.cfg map zm_tomb //
// Tranzit - exec zm_classic_transit.cfg map zm_transit //
// Tranzit Farm Survival - exec zm_standard_farm.cfg map zm_transit //
// Tranzit Town Survival - exec zm_standard_town.cfg map zm_transit //
// Tranzit Bus Depot Survival - exec zm_standard_transit.cfg map zm_transit //
// Tranzit Farm Grief - exec zm_grief_farm.cfg map zm_transit //
// Tranzit Town Grief - exec zm_grief_town.cfg map zm_transit //
// Tranzit Bus Depot Grief - exec zm_grief_transit.cfg map zm_transit //
// Tranzit Diner Turned - exec zm_cleansed_diner.cfg map zm_transit_dr //
//////////////////////////////////////////////////////////////////////////////
// Notes:
// Town/Tranzit servers will not natively spawn in tombstone since servers launch the maps in solo.
// --> https://forum.plutonium.pw/topic/124
// Grief requires a fix otherwise teams won't balance resulting in clients crashing when a 5th player joins.
// --> https://forum.plutonium.pw/topic/4057
//Classic/TranZit Maps rotation
sv_maprotation "exec zm_classic_prison.cfg map zm_prison"
//Survival Maps rotation
//sv_maprotation "exec zm_standard_town.cfg map zm_transit"
//Grief Maps rotation
//sv_maprotation "exec zm_grief_town.cfg map zm_transit exec zm_grief_transit.cfg map zm_transit exec zm_grief_farm.cfg map zm_transit exec zm_grief_cellblock.cfg map zm_prison exec zm_grief_street.cfg map zm_buried"
//Turned Maps rotation
//sv_maprotation "exec zm_cleansed_diner.cfg map zm_transit_dr exec zm_cleansed_street.cfg map zm_buried"
//Congratulations. You reached the end of this file. Leave map_rotate down below or else the server will not start after launch...
map_rotate

View file

@ -0,0 +1,93 @@
//////////////////////////////////////////////////
/// PlutoT6 ZM ServerConfiguration file //
///////////////////////////////////////////////////
// This config best view with Notepad++ OR //
// other *nix compatible editors of your choice. //
///////////////////////////////////////////////////
// Remove "//" in front of lines to allow the //
// server to read them. //
// Anything after "//" is a comment. //
//////////////////////////////////////////////////
// GENERAL SETTINGS //
//////////////////////////////////////////////////
//g_password "" // Require a password to join your server. (Use "password <yourpassword>" to set it on the client before connecting)
sv_maxclients 8 // Maximum players that are allowed in your server. (1-8, default is 4) Keeping this at 1-4 is recommended to prevent game breaking bugs.
//zombies_minplayers 1 // Minimum players needed to start the game (1-8, default is 1). Do NOT set this higher than sv_maxclients!
//sv_minPing 0 // Minimum ping needed to the server? (NOT recommended to edit, terribly broken and inaccurate since ages!)
//sv_maxPing 400 // Maximum ping allowed to the server? (NOT recommended to edit, terribly broken and inaccurate since ages!)
//zm_gungame 1 // Enable Pluto's custom Gun Game?
//zm_sharpshooter 1 // Enable Pluto's custom Sharp Shooter?
//gts zmDifficulty 1 // Difficulty? 0 = Easy, 1 = Normal !!If set to easy it must be manually set on the client as well!!
demo_enabled 0 // Record matches as demo files? 1 = Enabled, 0 = Disabled (Very efficient <5MB per match for a full server)
sv_sayname "" // name server-side 'say' commands show up as
sv_voice "1" // Allow Voice Chat (0 = Disable 1 = Everyone hears you. 2 = Teams only)
sv_voicequality "9" // Voice Chat Quality. (0-9) Default is 3 (= Steam/Console quality). Use 9 for the best quality.
sv_allowAimAssist 1 // Allow Aim Assist on gamepads. (0 = Will lock the option on gamepad controls menu.)
sv_fix_zm_weapons true // Fix the SMR's ADS spread, 870 MCS's penetration damage and allow sprinting with Galvaknuckles
sv_patch_zm_weapons true // Apply Post DLC1 changes to tar21_zm, type95_zm, xm8_zm, an94_zm, hamr_zm, rpd_zm, pdw57_zm, kard_zm ? (only recoil changes)
//////////////////////////////////////////////////
//These options can also be configured individually on a map basis in each zm config in gamesettings.
//////////////////////////////////////////////////
//gts startRound 1 // Starting Round. Works on all maps.
//gts magic 1 // Remove all supernatural assistance? Only Survival and Grief have this option!
//gts headshotsonly 0 // Headshots only? Only Survival and Grief have this option!
//gts allowdogs 1 // Allow Hellhounds? Only Survival has this option!
//gts cleansedLoadout 1 // Allow players to choose their Loadout? Only Turned has this option!
//gts teamCount 2 // Sets the number of teams 2. Set to 2 by default when loading grief.
//////////////////////////////////////////////////
// B3, IW4MADMIN, GAME LOG & RCON SETTINGS //
//////////////////////////////////////////////////
g_logSync 2 // 0 only flush on game end, 1 flush when buffer full, 2 always flush after a write, 3 append to old logs. (Keep this at 2 if you plan on using a 3rd party admin tool)
g_log "logs\games_zmnuketown.log" // If you choose to use this make sure the filename is unique for each server!
rcon_password "rconAQW25wqa" // RemoteCONtrol password. !!Do NOT skip if you plan on using a 3rd party admin tool such as B3 or IW4m-Admin!!
rcon_rate_limit "0" // Rate limit RCon; limit is per IP; range is 0 to 10 000; value is in milliseconds. Lower this if you use IW4mA's Game Interface.
rconWhitelistAdd "127.0.0.1" // Command used to add an IP address to the whitelist. When no IP is added all IPs can send rcon commands.
rconWhitelistAdd "198.251.84.64" // If it is set only the whitelisted IPs and loopback (127.0.0.0/8) can send commands.
seta sv_wwwBaseURL "" // Configure the URL to Fast DL mods from. (i.e. http://domain.tld/iw5)
seta fs_game "" // What mod are we loading? (i.e. "mods/MyMod")
//////////////////////////////////////////////////
//The "exec zm_<gametype>_<location>.cfg" line sets the dvars for the location and gametype for you. This .cfg applies to all following maps in the rotation like MP until another .cfg is defined.
//You may modify these .cfgs in gamesettings. Make sure only one sv_maprotation line is uncommented or you may run into errors on starting or joining the server.
//Currently rotating the map using sv_maprotation doesn't work properly, i.e. clients will be kicked with an error when the map rotates to another map.
//It's recommended to only include one map in your sv_maprotation for this reason.
//////////////////////////////////////////////////////////////////////////////
// Maps and the matching configs //
//////////////////////////////////////////////////////////////////////////////
// Buried - exec zm_classic_processing.cfg map zm_buried //
// Buried Turned - exec zm_cleansed_street.cfg map zm_buried //
// Buried Grief - exec zm_grief_street.cfg map zm_buried //
// Die Rise - exec zm_classic_rooftop.cfg map zm_highrise //
// Mob of The Dead - exec zm_classic_prison.cfg map zm_prison //
// Mob of The Dead Grief - exec zm_grief_cellblock.cfg map zm_prison //
// Nuketown - exec zm_standard_nuked.cfg map zm_nuked //
// Origins - exec zm_classic_tomb.cfg map zm_tomb //
// Tranzit - exec zm_classic_transit.cfg map zm_transit //
// Tranzit Farm Survival - exec zm_standard_farm.cfg map zm_transit //
// Tranzit Town Survival - exec zm_standard_town.cfg map zm_transit //
// Tranzit Bus Depot Survival - exec zm_standard_transit.cfg map zm_transit //
// Tranzit Farm Grief - exec zm_grief_farm.cfg map zm_transit //
// Tranzit Town Grief - exec zm_grief_town.cfg map zm_transit //
// Tranzit Bus Depot Grief - exec zm_grief_transit.cfg map zm_transit //
// Tranzit Diner Turned - exec zm_cleansed_diner.cfg map zm_transit_dr //
//////////////////////////////////////////////////////////////////////////////
// Notes:
// Town/Tranzit servers will not natively spawn in tombstone since servers launch the maps in solo.
// --> https://forum.plutonium.pw/topic/124
// Grief requires a fix otherwise teams won't balance resulting in clients crashing when a 5th player joins.
// --> https://forum.plutonium.pw/topic/4057
//Classic/TranZit Maps rotation
sv_maprotation "exec zm_standard_nuked.cfg map zm_nuked"
//Survival Maps rotation
//sv_maprotation "exec zm_standard_town.cfg map zm_transit"
//Grief Maps rotation
//sv_maprotation "exec zm_grief_town.cfg map zm_transit exec zm_grief_transit.cfg map zm_transit exec zm_grief_farm.cfg map zm_transit exec zm_grief_cellblock.cfg map zm_prison exec zm_grief_street.cfg map zm_buried"
//Turned Maps rotation
//sv_maprotation "exec zm_cleansed_diner.cfg map zm_transit_dr exec zm_cleansed_street.cfg map zm_buried"
//Congratulations. You reached the end of this file. Leave map_rotate down below or else the server will not start after launch...
map_rotate

93
t6/dedicated_zmorigin.cfg Normal file
View file

@ -0,0 +1,93 @@
//////////////////////////////////////////////////
/// PlutoT6 ZM ServerConfiguration file //
///////////////////////////////////////////////////
// This config best view with Notepad++ OR //
// other *nix compatible editors of your choice. //
///////////////////////////////////////////////////
// Remove "//" in front of lines to allow the //
// server to read them. //
// Anything after "//" is a comment. //
//////////////////////////////////////////////////
// GENERAL SETTINGS //
//////////////////////////////////////////////////
g_password "" // Require a password to join your server. (Use "password <yourpassword>" to set it on the client before connecting)
sv_maxclients 4 // Maximum players that are allowed in your server. (1-8, default is 4) Keeping this at 1-4 is recommended to prevent game breaking bugs.
//zombies_minplayers 4 // Minimum players needed to start the game (1-8, default is 1). Do NOT set this higher than sv_maxclients!
//sv_minPing 0 // Minimum ping needed to the server? (NOT recommended to edit, terribly broken and inaccurate since ages!)
//sv_maxPing 400 // Maximum ping allowed to the server? (NOT recommended to edit, terribly broken and inaccurate since ages!)
//zm_gungame 1 // Enable Pluto's custom Gun Game?
//zm_sharpshooter 1 // Enable Pluto's custom Sharp Shooter?
//gts zmDifficulty 1 // Difficulty? 0 = Easy, 1 = Normal !!If set to easy it must be manually set on the client as well!!
demo_enabled 0 // Record matches as demo files? 1 = Enabled, 0 = Disabled (Very efficient <5MB per match for a full server)
sv_sayname "" // name server-side 'say' commands show up as
sv_voice "1" // Allow Voice Chat (0 = Disable 1 = Everyone hears you. 2 = Teams only)
sv_voicequality "9" // Voice Chat Quality. (0-9) Default is 3 (= Steam/Console quality). Use 9 for the best quality.
sv_allowAimAssist 1 // Allow Aim Assist on gamepads. (0 = Will lock the option on gamepad controls menu.)
sv_fix_zm_weapons true // Fix the SMR's ADS spread, 870 MCS's penetration damage and allow sprinting with Galvaknuckles
sv_patch_zm_weapons true // Apply Post DLC1 changes to tar21_zm, type95_zm, xm8_zm, an94_zm, hamr_zm, rpd_zm, pdw57_zm, kard_zm ? (only recoil changes)
//////////////////////////////////////////////////
//These options can also be configured individually on a map basis in each zm config in gamesettings.
//////////////////////////////////////////////////
//gts startRound 1 // Starting Round. Works on all maps.
//gts magic 1 // Remove all supernatural assistance? Only Survival and Grief have this option!
//gts headshotsonly 0 // Headshots only? Only Survival and Grief have this option!
//gts allowdogs 1 // Allow Hellhounds? Only Survival has this option!
//gts cleansedLoadout 1 // Allow players to choose their Loadout? Only Turned has this option!
//gts teamCount 2 // Sets the number of teams 2. Set to 2 by default when loading grief.
//////////////////////////////////////////////////
// B3, IW4MADMIN, GAME LOG & RCON SETTINGS //
//////////////////////////////////////////////////
g_logSync 2 // 0 only flush on game end, 1 flush when buffer full, 2 always flush after a write, 3 append to old logs. (Keep this at 2 if you plan on using a 3rd party admin tool)
g_log "logs\games_zmorigin.log" // If you choose to use this make sure the filename is unique for each server!
rcon_password "rconAQW25wqa" // RemoteCONtrol password. !!Do NOT skip if you plan on using a 3rd party admin tool such as B3 or IW4m-Admin!!
rcon_rate_limit "0" // Rate limit RCon; limit is per IP; range is 0 to 10 000; value is in milliseconds. Lower this if you use IW4mA's Game Interface.
rconWhitelistAdd "127.0.0.1" // Command used to add an IP address to the whitelist. When no IP is added all IPs can send rcon commands.
rconWhitelistAdd "198.251.84.64" // If it is set only the whitelisted IPs and loopback (127.0.0.0/8) can send commands.
seta sv_wwwBaseURL "" // Configure the URL to Fast DL mods from. (i.e. http://domain.tld/iw5)
seta fs_game "" // What mod are we loading? (i.e. "mods/MyMod")
//////////////////////////////////////////////////
//The "exec zm_<gametype>_<location>.cfg" line sets the dvars for the location and gametype for you. This .cfg applies to all following maps in the rotation like MP until another .cfg is defined.
//You may modify these .cfgs in gamesettings. Make sure only one sv_maprotation line is uncommented or you may run into errors on starting or joining the server.
//Currently rotating the map using sv_maprotation doesn't work properly, i.e. clients will be kicked with an error when the map rotates to another map.
//It's recommended to only include one map in your sv_maprotation for this reason.
//////////////////////////////////////////////////////////////////////////////
// Maps and the matching configs //
//////////////////////////////////////////////////////////////////////////////
// Buried - exec zm_classic_processing.cfg map zm_buried //
// Buried Turned - exec zm_cleansed_street.cfg map zm_buried //
// Buried Grief - exec zm_grief_street.cfg map zm_buried //
// Die Rise - exec zm_classic_rooftop.cfg map zm_highrise //
// Mob of The Dead - exec zm_classic_prison.cfg map zm_prison //
// Mob of The Dead Grief - exec zm_grief_cellblock.cfg map zm_prison //
// Nuketown - exec zm_standard_nuked.cfg map zm_nuked //
// Origins - exec zm_classic_tomb.cfg map zm_tomb //
// Tranzit - exec zm_classic_transit.cfg map zm_transit //
// Tranzit Farm Survival - exec zm_standard_farm.cfg map zm_transit //
// Tranzit Town Survival - exec zm_standard_town.cfg map zm_transit //
// Tranzit Bus Depot Survival - exec zm_standard_transit.cfg map zm_transit //
// Tranzit Farm Grief - exec zm_grief_farm.cfg map zm_transit //
// Tranzit Town Grief - exec zm_grief_town.cfg map zm_transit //
// Tranzit Bus Depot Grief - exec zm_grief_transit.cfg map zm_transit //
// Tranzit Diner Turned - exec zm_cleansed_diner.cfg map zm_transit_dr //
//////////////////////////////////////////////////////////////////////////////
// Notes:
// Town/Tranzit servers will not natively spawn in tombstone since servers launch the maps in solo.
// --> https://forum.plutonium.pw/topic/124
// Grief requires a fix otherwise teams won't balance resulting in clients crashing when a 5th player joins.
// --> https://forum.plutonium.pw/topic/4057
//Classic/TranZit Maps rotation
sv_maprotation "exec zm_classic_tomb.cfg map zm_tomb"
//Survival Maps rotation
//sv_maprotation "exec zm_standard_town.cfg map zm_transit"
//Grief Maps rotation
//sv_maprotation "exec zm_grief_town.cfg map zm_transit exec zm_grief_transit.cfg map zm_transit exec zm_grief_farm.cfg map zm_transit exec zm_grief_cellblock.cfg map zm_prison exec zm_grief_street.cfg map zm_buried"
//Turned Maps rotation
//sv_maprotation "exec zm_cleansed_diner.cfg map zm_transit_dr exec zm_cleansed_street.cfg map zm_buried"
//Congratulations. You reached the end of this file. Leave map_rotate down below or else the server will not start after launch...
map_rotate

View file

@ -0,0 +1,93 @@
//////////////////////////////////////////////////
/// PlutoT6 ZM ServerConfiguration file //
///////////////////////////////////////////////////
// This config best view with Notepad++ OR //
// other *nix compatible editors of your choice. //
///////////////////////////////////////////////////
// Remove "//" in front of lines to allow the //
// server to read them. //
// Anything after "//" is a comment. //
//////////////////////////////////////////////////
// GENERAL SETTINGS //
//////////////////////////////////////////////////
//g_password "aaa" // Require a password to join your server. (Use "password <yourpassword>" to set it on the client before connecting)
sv_maxclients 4 // Maximum players that are allowed in your server. (1-8, default is 4) Keeping this at 1-4 is recommended to prevent game breaking bugs.
//zombies_minplayers 1 // Minimum players needed to start the game (1-8, default is 1). Do NOT set this higher than sv_maxclients!
//sv_minPing 0 // Minimum ping needed to the server? (NOT recommended to edit, terribly broken and inaccurate since ages!)
//sv_maxPing 400 // Maximum ping allowed to the server? (NOT recommended to edit, terribly broken and inaccurate since ages!)
//zm_gungame 1 // Enable Pluto's custom Gun Game?
//zm_sharpshooter 1 // Enable Pluto's custom Sharp Shooter?
//gts zmDifficulty 1 // Difficulty? 0 = Easy, 1 = Normal !!If set to easy it must be manually set on the client as well!!
demo_enabled 0 // Record matches as demo files? 1 = Enabled, 0 = Disabled (Very efficient <5MB per match for a full server)
sv_sayname "" // name server-side 'say' commands show up as
sv_voice "1" // Allow Voice Chat (0 = Disable 1 = Everyone hears you. 2 = Teams only)
sv_voicequality "9" // Voice Chat Quality. (0-9) Default is 3 (= Steam/Console quality). Use 9 for the best quality.
sv_allowAimAssist 1 // Allow Aim Assist on gamepads. (0 = Will lock the option on gamepad controls menu.)
sv_fix_zm_weapons true // Fix the SMR's ADS spread, 870 MCS's penetration damage and allow sprinting with Galvaknuckles
sv_patch_zm_weapons true // Apply Post DLC1 changes to tar21_zm, type95_zm, xm8_zm, an94_zm, hamr_zm, rpd_zm, pdw57_zm, kard_zm ? (only recoil changes)
//////////////////////////////////////////////////
//These options can also be configured individually on a map basis in each zm config in gamesettings.
//////////////////////////////////////////////////
//gts startRound 1 // Starting Round. Works on all maps.
//gts magic 1 // Remove all supernatural assistance? Only Survival and Grief have this option!
//gts headshotsonly 0 // Headshots only? Only Survival and Grief have this option!
//gts allowdogs 1 // Allow Hellhounds? Only Survival has this option!
//gts cleansedLoadout 1 // Allow players to choose their Loadout? Only Turned has this option!
//gts teamCount 2 // Sets the number of teams 2. Set to 2 by default when loading grief.
//////////////////////////////////////////////////
// B3, IW4MADMIN, GAME LOG & RCON SETTINGS //
//////////////////////////////////////////////////
g_logSync 2 // 0 only flush on game end, 1 flush when buffer full, 2 always flush after a write, 3 append to old logs. (Keep this at 2 if you plan on using a 3rd party admin tool)
g_log "logs\games_zmorigin2.log" // If you choose to use this make sure the filename is unique for each server!
rcon_password "rconAQW25wqa" // RemoteCONtrol password. !!Do NOT skip if you plan on using a 3rd party admin tool such as B3 or IW4m-Admin!!
rcon_rate_limit "0" // Rate limit RCon; limit is per IP; range is 0 to 10 000; value is in milliseconds. Lower this if you use IW4mA's Game Interface.
rconWhitelistAdd "127.0.0.1" // Command used to add an IP address to the whitelist. When no IP is added all IPs can send rcon commands.
rconWhitelistAdd "198.251.84.64" // If it is set only the whitelisted IPs and loopback (127.0.0.0/8) can send commands.
seta sv_wwwBaseURL "" // Configure the URL to Fast DL mods from. (i.e. http://domain.tld/iw5)
seta fs_game "" // What mod are we loading? (i.e. "mods/MyMod")
//////////////////////////////////////////////////
//The "exec zm_<gametype>_<location>.cfg" line sets the dvars for the location and gametype for you. This .cfg applies to all following maps in the rotation like MP until another .cfg is defined.
//You may modify these .cfgs in gamesettings. Make sure only one sv_maprotation line is uncommented or you may run into errors on starting or joining the server.
//Currently rotating the map using sv_maprotation doesn't work properly, i.e. clients will be kicked with an error when the map rotates to another map.
//It's recommended to only include one map in your sv_maprotation for this reason.
//////////////////////////////////////////////////////////////////////////////
// Maps and the matching configs //
//////////////////////////////////////////////////////////////////////////////
// Buried - exec zm_classic_processing.cfg map zm_buried //
// Buried Turned - exec zm_cleansed_street.cfg map zm_buried //
// Buried Grief - exec zm_grief_street.cfg map zm_buried //
// Die Rise - exec zm_classic_rooftop.cfg map zm_highrise //
// Mob of The Dead - exec zm_classic_prison.cfg map zm_prison //
// Mob of The Dead Grief - exec zm_grief_cellblock.cfg map zm_prison //
// Nuketown - exec zm_standard_nuked.cfg map zm_nuked //
// Origins - exec zm_classic_tomb.cfg map zm_tomb //
// Tranzit - exec zm_classic_transit.cfg map zm_transit //
// Tranzit Farm Survival - exec zm_standard_farm.cfg map zm_transit //
// Tranzit Town Survival - exec zm_standard_town.cfg map zm_transit //
// Tranzit Bus Depot Survival - exec zm_standard_transit.cfg map zm_transit //
// Tranzit Farm Grief - exec zm_grief_farm.cfg map zm_transit //
// Tranzit Town Grief - exec zm_grief_town.cfg map zm_transit //
// Tranzit Bus Depot Grief - exec zm_grief_transit.cfg map zm_transit //
// Tranzit Diner Turned - exec zm_cleansed_diner.cfg map zm_transit_dr //
//////////////////////////////////////////////////////////////////////////////
// Notes:
// Town/Tranzit servers will not natively spawn in tombstone since servers launch the maps in solo.
// --> https://forum.plutonium.pw/topic/124
// Grief requires a fix otherwise teams won't balance resulting in clients crashing when a 5th player joins.
// --> https://forum.plutonium.pw/topic/4057
//Classic/TranZit Maps rotation
sv_maprotation "exec zm_classic_tomb.cfg map zm_tomb"
//Survival Maps rotation
//sv_maprotation "exec zm_standard_town.cfg map zm_transit"
//Grief Maps rotation
//sv_maprotation "exec zm_grief_town.cfg map zm_transit exec zm_grief_transit.cfg map zm_transit exec zm_grief_farm.cfg map zm_transit exec zm_grief_cellblock.cfg map zm_prison exec zm_grief_street.cfg map zm_buried"
//Turned Maps rotation
//sv_maprotation "exec zm_cleansed_diner.cfg map zm_transit_dr exec zm_cleansed_street.cfg map zm_buried"
//Congratulations. You reached the end of this file. Leave map_rotate down below or else the server will not start after launch...
map_rotate

93
t6/dedicated_zmpanzer.cfg Normal file
View file

@ -0,0 +1,93 @@
//////////////////////////////////////////////////
/// PlutoT6 ZM ServerConfiguration file //
///////////////////////////////////////////////////
// This config best view with Notepad++ OR //
// other *nix compatible editors of your choice. //
///////////////////////////////////////////////////
// Remove "//" in front of lines to allow the //
// server to read them. //
// Anything after "//" is a comment. //
//////////////////////////////////////////////////
// GENERAL SETTINGS //
//////////////////////////////////////////////////
//g_password "panzos" // Require a password to join your server. (Use "password <yourpassword>" to set it on the client before connecting)
sv_maxclients 8 // Maximum players that are allowed in your server. (1-8, default is 4) Keeping this at 1-4 is recommended to prevent game breaking bugs.
//zombies_minplayers 2 // Minimum players needed to start the game (1-8, default is 1). Do NOT set this higher than sv_maxclients!
//sv_minPing 0 // Minimum ping needed to the server? (NOT recommended to edit, terribly broken and inaccurate since ages!)
//sv_maxPing 400 // Maximum ping allowed to the server? (NOT recommended to edit, terribly broken and inaccurate since ages!)
//zm_gungame 1 // Enable Pluto's custom Gun Game?
//zm_sharpshooter 1 // Enable Pluto's custom Sharp Shooter?
//gts zmDifficulty 1 // Difficulty? 0 = Easy, 1 = Normal !!If set to easy it must be manually set on the client as well!!
demo_enabled 0 // Record matches as demo files? 1 = Enabled, 0 = Disabled (Very efficient <5MB per match for a full server)
sv_sayname "" // name server-side 'say' commands show up as
sv_voice "1" // Allow Voice Chat (0 = Disable 1 = Everyone hears you. 2 = Teams only)
sv_voicequality "9" // Voice Chat Quality. (0-9) Default is 3 (= Steam/Console quality). Use 9 for the best quality.
sv_allowAimAssist 1 // Allow Aim Assist on gamepads. (0 = Will lock the option on gamepad controls menu.)
sv_fix_zm_weapons true // Fix the SMR's ADS spread, 870 MCS's penetration damage and allow sprinting with Galvaknuckles
sv_patch_zm_weapons true // Apply Post DLC1 changes to tar21_zm, type95_zm, xm8_zm, an94_zm, hamr_zm, rpd_zm, pdw57_zm, kard_zm ? (only recoil changes)
//////////////////////////////////////////////////
//These options can also be configured individually on a map basis in each zm config in gamesettings.
//////////////////////////////////////////////////
//gts startRound 1 // Starting Round. Works on all maps.
//gts magic 1 // Remove all supernatural assistance? Only Survival and Grief have this option!
//gts headshotsonly 0 // Headshots only? Only Survival and Grief have this option!
//gts allowdogs 1 // Allow Hellhounds? Only Survival has this option!
//gts cleansedLoadout 1 // Allow players to choose their Loadout? Only Turned has this option!
//gts teamCount 2 // Sets the number of teams 2. Set to 2 by default when loading grief.
//////////////////////////////////////////////////
// B3, IW4MADMIN, GAME LOG & RCON SETTINGS //
//////////////////////////////////////////////////
g_logSync 2 // 0 only flush on game end, 1 flush when buffer full, 2 always flush after a write, 3 append to old logs. (Keep this at 2 if you plan on using a 3rd party admin tool)
g_log "logs\games_zmpanzer.log" // If you choose to use this make sure the filename is unique for each server!
rcon_password "rconAQW25wqa" // RemoteCONtrol password. !!Do NOT skip if you plan on using a 3rd party admin tool such as B3 or IW4m-Admin!!
rcon_rate_limit "0" // Rate limit RCon; limit is per IP; range is 0 to 10 000; value is in milliseconds. Lower this if you use IW4mA's Game Interface.
rconWhitelistAdd "127.0.0.1" // Command used to add an IP address to the whitelist. When no IP is added all IPs can send rcon commands.
rconWhitelistAdd "198.251.84.64" // If it is set only the whitelisted IPs and loopback (127.0.0.0/8) can send commands.
seta sv_wwwBaseURL "" // Configure the URL to Fast DL mods from. (i.e. http://domain.tld/iw5)
seta fs_game "" // What mod are we loading? (i.e. "mods/MyMod")
//////////////////////////////////////////////////
//The "exec zm_<gametype>_<location>.cfg" line sets the dvars for the location and gametype for you. This .cfg applies to all following maps in the rotation like MP until another .cfg is defined.
//You may modify these .cfgs in gamesettings. Make sure only one sv_maprotation line is uncommented or you may run into errors on starting or joining the server.
//Currently rotating the map using sv_maprotation doesn't work properly, i.e. clients will be kicked with an error when the map rotates to another map.
//It's recommended to only include one map in your sv_maprotation for this reason.
//////////////////////////////////////////////////////////////////////////////
// Maps and the matching configs //
//////////////////////////////////////////////////////////////////////////////
// Buried - exec zm_classic_processing.cfg map zm_buried //
// Buried Turned - exec zm_cleansed_street.cfg map zm_buried //
// Buried Grief - exec zm_grief_street.cfg map zm_buried //
// Die Rise - exec zm_classic_rooftop.cfg map zm_highrise //
// Mob of The Dead - exec zm_classic_prison.cfg map zm_prison //
// Mob of The Dead Grief - exec zm_grief_cellblock.cfg map zm_prison //
// Nuketown - exec zm_standard_nuked.cfg map zm_nuked //
// Origins - exec zm_classic_tomb.cfg map zm_tomb //
// Tranzit - exec zm_classic_transit.cfg map zm_transit //
// Tranzit Farm Survival - exec zm_standard_farm.cfg map zm_transit //
// Tranzit Town Survival - exec zm_standard_town.cfg map zm_transit //
// Tranzit Bus Depot Survival - exec zm_standard_transit.cfg map zm_transit //
// Tranzit Farm Grief - exec zm_grief_farm.cfg map zm_transit //
// Tranzit Town Grief - exec zm_grief_town.cfg map zm_transit //
// Tranzit Bus Depot Grief - exec zm_grief_transit.cfg map zm_transit //
// Tranzit Diner Turned - exec zm_cleansed_diner.cfg map zm_transit_dr //
//////////////////////////////////////////////////////////////////////////////
// Notes:
// Town/Tranzit servers will not natively spawn in tombstone since servers launch the maps in solo.
// --> https://forum.plutonium.pw/topic/124
// Grief requires a fix otherwise teams won't balance resulting in clients crashing when a 5th player joins.
// --> https://forum.plutonium.pw/topic/4057
//Classic/TranZit Maps rotation
sv_maprotation "exec zm_classic_tomb.cfg map zm_tomb"
//Survival Maps rotation
//sv_maprotation "exec zm_standard_town.cfg map zm_transit"
//Grief Maps rotation
//sv_maprotation "exec zm_grief_town.cfg map zm_transit exec zm_grief_transit.cfg map zm_transit exec zm_grief_farm.cfg map zm_transit exec zm_grief_cellblock.cfg map zm_prison exec zm_grief_street.cfg map zm_buried"
//Turned Maps rotation
//sv_maprotation "exec zm_cleansed_diner.cfg map zm_transit_dr exec zm_cleansed_street.cfg map zm_buried"
//Congratulations. You reached the end of this file. Leave map_rotate down below or else the server will not start after launch...
map_rotate

View file

@ -0,0 +1,93 @@
//////////////////////////////////////////////////
/// PlutoT6 ZM ServerConfiguration file //
///////////////////////////////////////////////////
// This config best view with Notepad++ OR //
// other *nix compatible editors of your choice. //
///////////////////////////////////////////////////
// Remove "//" in front of lines to allow the //
// server to read them. //
// Anything after "//" is a comment. //
//////////////////////////////////////////////////
// GENERAL SETTINGS //
//////////////////////////////////////////////////
//g_password "tournamentultimiss" // Require a password to join your server. (Use "password <yourpassword>" to set it on the client before connecting)
sv_maxclients 8 // Maximum players that are allowed in your server. (1-8, default is 4) Keeping this at 1-4 is recommended to prevent game breaking bugs.
//zombies_minplayers 3 // Minimum players needed to start the game (1-8, default is 1). Do NOT set this higher than sv_maxclients!
//sv_minPing 0 // Minimum ping needed to the server? (NOT recommended to edit, terribly broken and inaccurate since ages!)
//sv_maxPing 400 // Maximum ping allowed to the server? (NOT recommended to edit, terribly broken and inaccurate since ages!)
//zm_gungame 1 // Enable Pluto's custom Gun Game?
//zm_sharpshooter 1 // Enable Pluto's custom Sharp Shooter?
//gts zmDifficulty 1 // Difficulty? 0 = Easy, 1 = Normal !!If set to easy it must be manually set on the client as well!!
demo_enabled 0 // Record matches as demo files? 1 = Enabled, 0 = Disabled (Very efficient <5MB per match for a full server)
sv_sayname "" // name server-side 'say' commands show up as
sv_voice "1" // Allow Voice Chat (0 = Disable 1 = Everyone hears you. 2 = Teams only)
sv_voicequality "9" // Voice Chat Quality. (0-9) Default is 3 (= Steam/Console quality). Use 9 for the best quality.
sv_allowAimAssist 1 // Allow Aim Assist on gamepads. (0 = Will lock the option on gamepad controls menu.)
sv_fix_zm_weapons true // Fix the SMR's ADS spread, 870 MCS's penetration damage and allow sprinting with Galvaknuckles
sv_patch_zm_weapons true // Apply Post DLC1 changes to tar21_zm, type95_zm, xm8_zm, an94_zm, hamr_zm, rpd_zm, pdw57_zm, kard_zm ? (only recoil changes)
//////////////////////////////////////////////////
//These options can also be configured individually on a map basis in each zm config in gamesettings.
//////////////////////////////////////////////////
//gts startRound 1 // Starting Round. Works on all maps.
//gts magic 1 // Remove all supernatural assistance? Only Survival and Grief have this option!
//gts headshotsonly 0 // Headshots only? Only Survival and Grief have this option!
//gts allowdogs 1 // Allow Hellhounds? Only Survival has this option!
//gts cleansedLoadout 1 // Allow players to choose their Loadout? Only Turned has this option!
//gts teamCount 2 // Sets the number of teams 2. Set to 2 by default when loading grief.
//////////////////////////////////////////////////
// B3, IW4MADMIN, GAME LOG & RCON SETTINGS //
//////////////////////////////////////////////////
g_logSync 2 // 0 only flush on game end, 1 flush when buffer full, 2 always flush after a write, 3 append to old logs. (Keep this at 2 if you plan on using a 3rd party admin tool)
g_log "logs\games_zmprivate.log" // If you choose to use this make sure the filename is unique for each server!
rcon_password "rconAQW25wqa" // RemoteCONtrol password. !!Do NOT skip if you plan on using a 3rd party admin tool such as B3 or IW4m-Admin!!
rcon_rate_limit "0" // Rate limit RCon; limit is per IP; range is 0 to 10 000; value is in milliseconds. Lower this if you use IW4mA's Game Interface.
rconWhitelistAdd "127.0.0.1" // Command used to add an IP address to the whitelist. When no IP is added all IPs can send rcon commands.
rconWhitelistAdd "198.251.84.64" // If it is set only the whitelisted IPs and loopback (127.0.0.0/8) can send commands.
seta sv_wwwBaseURL "" // Configure the URL to Fast DL mods from. (i.e. http://domain.tld/iw5)
seta fs_game "" // What mod are we loading? (i.e. "mods/MyMod")
//////////////////////////////////////////////////
//The "exec zm_<gametype>_<location>.cfg" line sets the dvars for the location and gametype for you. This .cfg applies to all following maps in the rotation like MP until another .cfg is defined.
//You may modify these .cfgs in gamesettings. Make sure only one sv_maprotation line is uncommented or you may run into errors on starting or joining the server.
//Currently rotating the map using sv_maprotation doesn't work properly, i.e. clients will be kicked with an error when the map rotates to another map.
//It's recommended to only include one map in your sv_maprotation for this reason.
//////////////////////////////////////////////////////////////////////////////
// Maps and the matching configs //
//////////////////////////////////////////////////////////////////////////////
// Buried - exec zm_classic_processing.cfg map zm_buried //
// Buried Turned - exec zm_cleansed_street.cfg map zm_buried //
// Buried Grief - exec zm_grief_street.cfg map zm_buried //
// Die Rise - exec zm_classic_rooftop.cfg map zm_highrise //
// Mob of The Dead - exec zm_classic_prison.cfg map zm_prison //
// Mob of The Dead Grief - exec zm_grief_cellblock.cfg map zm_prison //
// Nuketown - exec zm_standard_nuked.cfg map zm_nuked //
// Origins - exec zm_classic_tomb.cfg map zm_tomb //
// Tranzit - exec zm_classic_transit.cfg map zm_transit //
// Tranzit Farm Survival - exec zm_standard_farm.cfg map zm_transit //
// Tranzit Town Survival - exec zm_standard_town.cfg map zm_transit //
// Tranzit Bus Depot Survival - exec zm_standard_transit.cfg map zm_transit //
// Tranzit Farm Grief - exec zm_grief_farm.cfg map zm_transit //
// Tranzit Town Grief - exec zm_grief_town.cfg map zm_transit //
// Tranzit Bus Depot Grief - exec zm_grief_transit.cfg map zm_transit //
// Tranzit Diner Turned - exec zm_cleansed_diner.cfg map zm_transit_dr //
//////////////////////////////////////////////////////////////////////////////
// Notes:
// Town/Tranzit servers will not natively spawn in tombstone since servers launch the maps in solo.
// --> https://forum.plutonium.pw/topic/124
// Grief requires a fix otherwise teams won't balance resulting in clients crashing when a 5th player joins.
// --> https://forum.plutonium.pw/topic/4057
//Classic/TranZit Maps rotation
sv_maprotation "exec zm_classic_prison.cfg map zm_prison"
//Survival Maps rotation
//sv_maprotation "exec zm_standard_town.cfg map zm_transit"
//Grief Maps rotation
//sv_maprotation "exec zm_grief_town.cfg map zm_transit exec zm_grief_transit.cfg map zm_transit exec zm_grief_farm.cfg map zm_transit exec zm_grief_cellblock.cfg map zm_prison exec zm_grief_street.cfg map zm_buried"
//Turned Maps rotation
//sv_maprotation "exec zm_cleansed_diner.cfg map zm_transit_dr exec zm_cleansed_street.cfg map zm_buried"
//Congratulations. You reached the end of this file. Leave map_rotate down below or else the server will not start after launch...
map_rotate

93
t6/dedicated_zmtitb.cfg Normal file
View file

@ -0,0 +1,93 @@
//////////////////////////////////////////////////
/// PlutoT6 ZM ServerConfiguration file //
///////////////////////////////////////////////////
// This config best view with Notepad++ OR //
// other *nix compatible editors of your choice. //
///////////////////////////////////////////////////
// Remove "//" in front of lines to allow the //
// server to read them. //
// Anything after "//" is a comment. //
//////////////////////////////////////////////////
// GENERAL SETTINGS //
//////////////////////////////////////////////////
//g_password "" // Require a password to join your server. (Use "password <yourpassword>" to set it on the client before connecting)
sv_maxclients 8 // Maximum players that are allowed in your server. (1-8, default is 4) Keeping this at 1-4 is recommended to prevent game breaking bugs.
//zombies_minplayers 1 // Minimum players needed to start the game (1-8, default is 1). Do NOT set this higher than sv_maxclients!
//sv_minPing 0 // Minimum ping needed to the server? (NOT recommended to edit, terribly broken and inaccurate since ages!)
//sv_maxPing 400 // Maximum ping allowed to the server? (NOT recommended to edit, terribly broken and inaccurate since ages!)
//zm_gungame 1 // Enable Pluto's custom Gun Game?
//zm_sharpshooter 1 // Enable Pluto's custom Sharp Shooter?
//gts zmDifficulty 1 // Difficulty? 0 = Easy, 1 = Normal !!If set to easy it must be manually set on the client as well!!
demo_enabled 0 // Record matches as demo files? 1 = Enabled, 0 = Disabled (Very efficient <5MB per match for a full server)
sv_sayname "" // name server-side 'say' commands show up as
sv_voice "1" // Allow Voice Chat (0 = Disable 1 = Everyone hears you. 2 = Teams only)
sv_voicequality "9" // Voice Chat Quality. (0-9) Default is 3 (= Steam/Console quality). Use 9 for the best quality.
sv_allowAimAssist 1 // Allow Aim Assist on gamepads. (0 = Will lock the option on gamepad controls menu.)
sv_fix_zm_weapons true // Fix the SMR's ADS spread, 870 MCS's penetration damage and allow sprinting with Galvaknuckles
sv_patch_zm_weapons true // Apply Post DLC1 changes to tar21_zm, type95_zm, xm8_zm, an94_zm, hamr_zm, rpd_zm, pdw57_zm, kard_zm ? (only recoil changes)
//////////////////////////////////////////////////
//These options can also be configured individually on a map basis in each zm config in gamesettings.
//////////////////////////////////////////////////
//gts startRound 1 // Starting Round. Works on all maps.
//gts magic 1 // Remove all supernatural assistance? Only Survival and Grief have this option!
//gts headshotsonly 0 // Headshots only? Only Survival and Grief have this option!
//gts allowdogs 1 // Allow Hellhounds? Only Survival has this option!
//gts cleansedLoadout 1 // Allow players to choose their Loadout? Only Turned has this option!
//gts teamCount 2 // Sets the number of teams 2. Set to 2 by default when loading grief.
//////////////////////////////////////////////////
// B3, IW4MADMIN, GAME LOG & RCON SETTINGS //
//////////////////////////////////////////////////
g_logSync 2 // 0 only flush on game end, 1 flush when buffer full, 2 always flush after a write, 3 append to old logs. (Keep this at 2 if you plan on using a 3rd party admin tool)
g_log "logs\games_zmtitb.log" // If you choose to use this make sure the filename is unique for each server!
rcon_password "rconAQW25wqa" // RemoteCONtrol password. !!Do NOT skip if you plan on using a 3rd party admin tool such as B3 or IW4m-Admin!!
rcon_rate_limit "0" // Rate limit RCon; limit is per IP; range is 0 to 10 000; value is in milliseconds. Lower this if you use IW4mA's Game Interface.
rconWhitelistAdd "127.0.0.1" // Command used to add an IP address to the whitelist. When no IP is added all IPs can send rcon commands.
rconWhitelistAdd "198.251.84.64" // If it is set only the whitelisted IPs and loopback (127.0.0.0/8) can send commands.
seta sv_wwwBaseURL "" // Configure the URL to Fast DL mods from. (i.e. http://domain.tld/iw5)
seta fs_game "" // What mod are we loading? (i.e. "mods/MyMod")
//////////////////////////////////////////////////
//The "exec zm_<gametype>_<location>.cfg" line sets the dvars for the location and gametype for you. This .cfg applies to all following maps in the rotation like MP until another .cfg is defined.
//You may modify these .cfgs in gamesettings. Make sure only one sv_maprotation line is uncommented or you may run into errors on starting or joining the server.
//Currently rotating the map using sv_maprotation doesn't work properly, i.e. clients will be kicked with an error when the map rotates to another map.
//It's recommended to only include one map in your sv_maprotation for this reason.
//////////////////////////////////////////////////////////////////////////////
// Maps and the matching configs //
//////////////////////////////////////////////////////////////////////////////
// Buried - exec zm_classic_processing.cfg map zm_buried //
// Buried Turned - exec zm_cleansed_street.cfg map zm_buried //
// Buried Grief - exec zm_grief_street.cfg map zm_buried //
// Die Rise - exec zm_classic_rooftop.cfg map zm_highrise //
// Mob of The Dead - exec zm_classic_prison.cfg map zm_prison //
// Mob of The Dead Grief - exec zm_grief_cellblock.cfg map zm_prison //
// Nuketown - exec zm_standard_nuked.cfg map zm_nuked //
// Origins - exec zm_classic_tomb.cfg map zm_tomb //
// Tranzit - exec zm_classic_transit.cfg map zm_transit //
// Tranzit Farm Survival - exec zm_standard_farm.cfg map zm_transit //
// Tranzit Town Survival - exec zm_standard_town.cfg map zm_transit //
// Tranzit Bus Depot Survival - exec zm_standard_transit.cfg map zm_transit //
// Tranzit Farm Grief - exec zm_grief_farm.cfg map zm_transit //
// Tranzit Town Grief - exec zm_grief_town.cfg map zm_transit //
// Tranzit Bus Depot Grief - exec zm_grief_transit.cfg map zm_transit //
// Tranzit Diner Turned - exec zm_cleansed_diner.cfg map zm_transit_dr //
//////////////////////////////////////////////////////////////////////////////
// Notes:
// Town/Tranzit servers will not natively spawn in tombstone since servers launch the maps in solo.
// --> https://forum.plutonium.pw/topic/124
// Grief requires a fix otherwise teams won't balance resulting in clients crashing when a 5th player joins.
// --> https://forum.plutonium.pw/topic/4057
//Classic/TranZit Maps rotation
sv_maprotation "exec zm_classic_transit.cfg map zm_transit"
//Survival Maps rotation
//sv_maprotation "exec zm_standard_town.cfg map zm_transit"
//Grief Maps rotation
//sv_maprotation "exec zm_grief_town.cfg map zm_transit exec zm_grief_transit.cfg map zm_transit exec zm_grief_farm.cfg map zm_transit exec zm_grief_cellblock.cfg map zm_prison exec zm_grief_street.cfg map zm_buried"
//Turned Maps rotation
//sv_maprotation "exec zm_cleansed_diner.cfg map zm_transit_dr exec zm_cleansed_street.cfg map zm_buried"
//Congratulations. You reached the end of this file. Leave map_rotate down below or else the server will not start after launch...
map_rotate

Some files were not shown because too many files have changed in this diff Show more