Full code added
This commit is contained in:
parent
d48fa5c54a
commit
cb9bea73ec
284 changed files with 94999 additions and 1 deletions
52
README.md
52
README.md
|
@ -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
41
bo2/!restart_servers.bat
Normal 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
|
23
bo2/!start_zm_serverbrutus.bat
Normal file
23
bo2/!start_zm_serverbrutus.bat
Normal 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
|
23
bo2/!start_zm_serverburied.bat
Normal file
23
bo2/!start_zm_serverburied.bat
Normal 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
|
23
bo2/!start_zm_serverdierise.bat
Normal file
23
bo2/!start_zm_serverdierise.bat
Normal 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
|
23
bo2/!start_zm_servermotd.bat
Normal file
23
bo2/!start_zm_servermotd.bat
Normal 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
|
23
bo2/!start_zm_servernuketown.bat
Normal file
23
bo2/!start_zm_servernuketown.bat
Normal 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
|
23
bo2/!start_zm_serverorigin.bat
Normal file
23
bo2/!start_zm_serverorigin.bat
Normal 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
|
23
bo2/!start_zm_serverorigin2.bat
Normal file
23
bo2/!start_zm_serverorigin2.bat
Normal 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
|
23
bo2/!start_zm_serverpanzer.bat
Normal file
23
bo2/!start_zm_serverpanzer.bat
Normal 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
|
23
bo2/!start_zm_serverprivate.bat
Normal file
23
bo2/!start_zm_serverprivate.bat
Normal 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
|
23
bo2/!start_zm_serverraid.bat
Normal file
23
bo2/!start_zm_serverraid.bat
Normal 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
|
23
bo2/!start_zm_servertitb.bat
Normal file
23
bo2/!start_zm_servertitb.bat
Normal 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
|
23
bo2/!start_zm_servertown.bat
Normal file
23
bo2/!start_zm_servertown.bat
Normal 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
|
23
bo2/!start_zm_servertown2.bat
Normal file
23
bo2/!start_zm_servertown2.bat
Normal 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
|
23
bo2/!start_zm_servertown3.bat
Normal file
23
bo2/!start_zm_servertown3.bat
Normal 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
|
23
bo2/!start_zm_servertranzit.bat
Normal file
23
bo2/!start_zm_servertranzit.bat
Normal 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
BIN
bo2/binkw32.dll
Normal file
Binary file not shown.
39
bo2/getCmdPID.bat
Normal file
39
bo2/getCmdPID.bat
Normal 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
BIN
bo2/getCmdPID.exe
Normal file
Binary file not shown.
BIN
bo2/wlanapi.dll
Normal file
BIN
bo2/wlanapi.dll
Normal file
Binary file not shown.
192
node-server-manager/Configuration/NSMConfiguration.json
Normal file
192
node-server-manager/Configuration/NSMConfiguration.json
Normal 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"
|
||||
}
|
||||
}
|
||||
}
|
84
node-server-manager/Lib/CLICommands.js
Normal file
84
node-server-manager/Lib/CLICommands.js
Normal 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
|
63
node-server-manager/Lib/Classes.js
Normal file
63
node-server-manager/Lib/Classes.js
Normal 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 }
|
16
node-server-manager/Lib/ClientData.js
Normal file
16
node-server-manager/Lib/ClientData.js
Normal 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
|
95
node-server-manager/Lib/Commands.js
Normal file
95
node-server-manager/Lib/Commands.js
Normal 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
|
139
node-server-manager/Lib/ConfigMaker.js
Normal file
139
node-server-manager/Lib/ConfigMaker.js
Normal 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
|
36
node-server-manager/Lib/DatabaseModels.js
Normal file
36
node-server-manager/Lib/DatabaseModels.js
Normal 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
|
106
node-server-manager/Lib/Entity/Player.js
Normal file
106
node-server-manager/Lib/Entity/Player.js
Normal 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
|
279
node-server-manager/Lib/Entity/Server.js
Normal file
279
node-server-manager/Lib/Entity/Server.js
Normal 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
|
BIN
node-server-manager/Lib/Entity/give_tb-compiled.gsc
Normal file
BIN
node-server-manager/Lib/Entity/give_tb-compiled.gsc
Normal file
Binary file not shown.
BIN
node-server-manager/Lib/Entity/setround-compiled.gsc
Normal file
BIN
node-server-manager/Lib/Entity/setround-compiled.gsc
Normal file
Binary file not shown.
BIN
node-server-manager/Lib/Entity/statsaprilfool-compiled.gsc
Normal file
BIN
node-server-manager/Lib/Entity/statsaprilfool-compiled.gsc
Normal file
Binary file not shown.
118
node-server-manager/Lib/EventDispatcher.js
Normal file
118
node-server-manager/Lib/EventDispatcher.js
Normal 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
|
54
node-server-manager/Lib/EventLogWatcher.js
Normal file
54
node-server-manager/Lib/EventLogWatcher.js
Normal 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
|
106
node-server-manager/Lib/EventParser.js
Normal file
106
node-server-manager/Lib/EventParser.js
Normal 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
|
1227
node-server-manager/Lib/InitDatabase.js
Normal file
1227
node-server-manager/Lib/InitDatabase.js
Normal file
File diff suppressed because it is too large
Load diff
151
node-server-manager/Lib/MasterServer.js
Normal file
151
node-server-manager/Lib/MasterServer.js
Normal 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
|
37
node-server-manager/Lib/Models/NSMAliases.js
Normal file
37
node-server-manager/Lib/Models/NSMAliases.js
Normal 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
|
||||
}
|
35
node-server-manager/Lib/Models/NSMAudit.js
Normal file
35
node-server-manager/Lib/Models/NSMAudit.js
Normal 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
|
||||
}
|
51
node-server-manager/Lib/Models/NSMClients.js
Normal file
51
node-server-manager/Lib/Models/NSMClients.js
Normal 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
|
||||
}
|
41
node-server-manager/Lib/Models/NSMConnections.js
Normal file
41
node-server-manager/Lib/Models/NSMConnections.js
Normal 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
|
||||
}
|
56
node-server-manager/Lib/Models/NSMKills.js
Normal file
56
node-server-manager/Lib/Models/NSMKills.js
Normal 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
|
||||
}
|
40
node-server-manager/Lib/Models/NSMMessages.js
Normal file
40
node-server-manager/Lib/Models/NSMMessages.js
Normal 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
|
||||
}
|
37
node-server-manager/Lib/Models/NSMMeta.js
Normal file
37
node-server-manager/Lib/Models/NSMMeta.js
Normal 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
|
||||
}
|
53
node-server-manager/Lib/Models/NSMPenalties.js
Normal file
53
node-server-manager/Lib/Models/NSMPenalties.js
Normal 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
|
||||
}
|
39
node-server-manager/Lib/Models/NSMPlayerStatHistory.js
Normal file
39
node-server-manager/Lib/Models/NSMPlayerStatHistory.js
Normal 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
|
||||
}
|
53
node-server-manager/Lib/Models/NSMPlayerStats.js
Normal file
53
node-server-manager/Lib/Models/NSMPlayerStats.js
Normal 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
|
||||
}
|
45
node-server-manager/Lib/Models/NSMReports.js
Normal file
45
node-server-manager/Lib/Models/NSMReports.js
Normal 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
|
||||
}
|
35
node-server-manager/Lib/Models/NSMSettings.js
Normal file
35
node-server-manager/Lib/Models/NSMSettings.js
Normal 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
|
||||
}
|
29
node-server-manager/Lib/Models/NSMTokens.js
Normal file
29
node-server-manager/Lib/Models/NSMTokens.js
Normal 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
|
||||
}
|
94
node-server-manager/Lib/NodeLogServer.js
Normal file
94
node-server-manager/Lib/NodeLogServer.js
Normal 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)
|
||||
})
|
184
node-server-manager/Lib/NodeServerManager.js
Normal file
184
node-server-manager/Lib/NodeServerManager.js
Normal 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()
|
||||
}
|
||||
|
42
node-server-manager/Lib/RconCommandPrefixes/Default.js
Normal file
42
node-server-manager/Lib/RconCommandPrefixes/Default.js
Normal 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
|
||||
}
|
||||
}
|
40
node-server-manager/Lib/RconCommandPrefixes/IW3.js
Normal file
40
node-server-manager/Lib/RconCommandPrefixes/IW3.js
Normal 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
|
||||
}
|
||||
}
|
39
node-server-manager/Lib/RconCommandPrefixes/IW4.js
Normal file
39
node-server-manager/Lib/RconCommandPrefixes/IW4.js
Normal 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
|
||||
}
|
||||
}
|
43
node-server-manager/Lib/RconCommandPrefixes/IW5.js
Normal file
43
node-server-manager/Lib/RconCommandPrefixes/IW5.js
Normal 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
|
||||
}
|
||||
}
|
39
node-server-manager/Lib/RconCommandPrefixes/IW6.js
Normal file
39
node-server-manager/Lib/RconCommandPrefixes/IW6.js
Normal 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
|
||||
}
|
||||
}
|
40
node-server-manager/Lib/RconCommandPrefixes/T4.js
Normal file
40
node-server-manager/Lib/RconCommandPrefixes/T4.js
Normal 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
|
||||
}
|
||||
}
|
39
node-server-manager/Lib/RconCommandPrefixes/T6.js
Normal file
39
node-server-manager/Lib/RconCommandPrefixes/T6.js
Normal 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
|
||||
}
|
||||
}
|
196
node-server-manager/Lib/RconConnection.js
Normal file
196
node-server-manager/Lib/RconConnection.js
Normal 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
|
56
node-server-manager/Lib/ServerLogWatcher.js
Normal file
56
node-server-manager/Lib/ServerLogWatcher.js
Normal 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.
BIN
node-server-manager/Plugins/.vs/Plugins/v17/.wsuo
Normal file
BIN
node-server-manager/Plugins/.vs/Plugins/v17/.wsuo
Normal file
Binary file not shown.
6
node-server-manager/Plugins/.vs/VSWorkspaceState.json
Normal file
6
node-server-manager/Plugins/.vs/VSWorkspaceState.json
Normal file
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"ExpandedNodes": [
|
||||
""
|
||||
],
|
||||
"PreviewInSolutionExplorer": false
|
||||
}
|
BIN
node-server-manager/Plugins/.vs/slnx.sqlite
Normal file
BIN
node-server-manager/Plugins/.vs/slnx.sqlite
Normal file
Binary file not shown.
205
node-server-manager/Plugins/AntiVPN.js
Normal file
205
node-server-manager/Plugins/AntiVPN.js
Normal 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
|
3894
node-server-manager/Plugins/ClanTag.js
Normal file
3894
node-server-manager/Plugins/ClanTag.js
Normal file
File diff suppressed because it is too large
Load diff
98
node-server-manager/Plugins/DiscordWebhook.js
Normal file
98
node-server-manager/Plugins/DiscordWebhook.js
Normal 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
|
84
node-server-manager/Plugins/Global/AutoMessages.js
Normal file
84
node-server-manager/Plugins/Global/AutoMessages.js
Normal 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
|
1012
node-server-manager/Plugins/Global/DiscordBot.js
Normal file
1012
node-server-manager/Plugins/Global/DiscordBot.js
Normal file
File diff suppressed because it is too large
Load diff
53
node-server-manager/Plugins/Global/GlobalChat.js
Normal file
53
node-server-manager/Plugins/Global/GlobalChat.js
Normal 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
|
31
node-server-manager/Plugins/Global/ProfileMeta.js
Normal file
31
node-server-manager/Plugins/Global/ProfileMeta.js
Normal 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
|
416
node-server-manager/Plugins/MoreCommands.js
Normal file
416
node-server-manager/Plugins/MoreCommands.js
Normal 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
|
800
node-server-manager/Plugins/NativeCommands.js
Normal file
800
node-server-manager/Plugins/NativeCommands.js
Normal 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
|
46
node-server-manager/Plugins/Penalties.js
Normal file
46
node-server-manager/Plugins/Penalties.js
Normal 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
|
50
node-server-manager/Plugins/Routes/Discord.js
Normal file
50
node-server-manager/Plugins/Routes/Discord.js
Normal 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)}`)
|
||||
})
|
||||
}
|
28
node-server-manager/Plugins/Routes/SocialMedia.js
Normal file
28
node-server-manager/Plugins/Routes/SocialMedia.js
Normal 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)
|
||||
}
|
41
node-server-manager/Plugins/Routes/ZStats.js
Normal file
41
node-server-manager/Plugins/Routes/ZStats.js
Normal 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)
|
||||
})
|
||||
})
|
||||
}
|
10
node-server-manager/Plugins/Routes/index.js
Normal file
10
node-server-manager/Plugins/Routes/index.js
Normal 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)
|
||||
})
|
||||
}
|
269
node-server-manager/Plugins/ScriptCommands.js
Normal file
269
node-server-manager/Plugins/ScriptCommands.js
Normal 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
|
85
node-server-manager/Plugins/StatLogger.js
Normal file
85
node-server-manager/Plugins/StatLogger.js
Normal 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
|
290
node-server-manager/Plugins/Voting.js
Normal file
290
node-server-manager/Plugins/Voting.js
Normal 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
|
88
node-server-manager/Plugins/WelcomeMessages.js
Normal file
88
node-server-manager/Plugins/WelcomeMessages.js
Normal 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
|
4627
node-server-manager/Plugins/ZombiesBank.js
Normal file
4627
node-server-manager/Plugins/ZombiesBank.js
Normal file
File diff suppressed because it is too large
Load diff
187
node-server-manager/Plugins/ZombiesLocker.js
Normal file
187
node-server-manager/Plugins/ZombiesLocker.js
Normal 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
|
390
node-server-manager/Plugins/ZombiesStats.js
Normal file
390
node-server-manager/Plugins/ZombiesStats.js
Normal 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
|
BIN
t6/compiled/t6/_zm_ai_brutus.gsc
Normal file
BIN
t6/compiled/t6/_zm_ai_brutus.gsc
Normal file
Binary file not shown.
BIN
t6/compiled/t6/panzerwave.gsc
Normal file
BIN
t6/compiled/t6/panzerwave.gsc
Normal file
Binary file not shown.
BIN
t6/compiled/t6/zm_alcatraz_brutus.gsc
Normal file
BIN
t6/compiled/t6/zm_alcatraz_brutus.gsc
Normal file
Binary file not shown.
93
t6/dedicated_zmbrutus.cfg
Normal file
93
t6/dedicated_zmbrutus.cfg
Normal 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
94
t6/dedicated_zmburied.cfg
Normal 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
|
93
t6/dedicated_zmdierise.cfg
Normal file
93
t6/dedicated_zmdierise.cfg
Normal 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
93
t6/dedicated_zmmotd.cfg
Normal 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
|
93
t6/dedicated_zmnuketown.cfg
Normal file
93
t6/dedicated_zmnuketown.cfg
Normal 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
93
t6/dedicated_zmorigin.cfg
Normal 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
|
93
t6/dedicated_zmorigin2.cfg
Normal file
93
t6/dedicated_zmorigin2.cfg
Normal 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
93
t6/dedicated_zmpanzer.cfg
Normal 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
|
93
t6/dedicated_zmprivate.cfg
Normal file
93
t6/dedicated_zmprivate.cfg
Normal 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
93
t6/dedicated_zmtitb.cfg
Normal 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
Loading…
Add table
Reference in a new issue