Skip to content

Commit 75a4448

Browse files
authored
Merge pull request #8 from Tiger-Tom/Version-3.0
Version 3.0
2 parents de8a0bc + a016b35 commit 75a4448

3 files changed

Lines changed: 111 additions & 28 deletions

File tree

Changelog.txt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,16 @@
1+
From bugfix 2.3 to release 3.0:
2+
3+
Additions:
4+
- Added mods, which can add custom ChatCommands and help strings, or modify already existing commands and help strings
5+
6+
Changes:
7+
- Changed the "refresh" ChatCommand help to reflect that it also reloads mods
8+
9+
Bugfixes:
10+
- Fixed a bug where the program wouldn't boot on Windows, because removing the "update" command returned an error as it was using an outdated name
11+
12+
--------------------------------------------------
13+
114
From QOL 2.2 to bugfix 2.3:
215

316
Bugfixes:

README.md

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ This script has the following features (besides interfacing with the Minecraft s
66

77
>- Support for Linux, and partial support for Windows (most things work on Windows, and things that don't are either disabled or have warnings)
88
>
9+
>- Support for modding to add new or modify existing ChatCommands and help strings (see https://github.com/Tiger-Tom/RunServerDotPy-extras/blob/main/ModExamples/MainModExample.py)
10+
>
911
>- Automatically backing up to a zip file every time the server starts (only on Linux)
1012
>
1113
>- Automatically restarting the server if it stops or crashes for whatever reason
@@ -28,26 +30,26 @@ This script has the following features (besides interfacing with the Minecraft s
2830
2931
_Fun fact: the original RunServer.py script that I made was around 825 lines, and my goal for the new version (this one) was to make it more efficient, but I still ended up with more lines (hopefully the extra lines are just because of better commenting, better spacing, and more features being added)_
3032

31-
Table of contents for version 2.3 (Generated by "RunServerTOC.py" in https://github.com/Tiger-Tom/RunServerDotPy-extras/)
33+
Table of contents for version 3.0 (Generated by "RunServerTOC.py" in https://github.com/Tiger-Tom/RunServerDotPy-extras/)
3234
-0000 ┌╢START OF FILE
3335
-0008 ├─Imports
3436
-0057 ├─Directories
35-
-0068 ├─Fix main RunServerDotPy directory if missing
36-
-0071 ├─Setup some module-related things
37-
-0074 ├─Setup some values
38-
-0078 ├─Setup profanity filter
39-
-0085 ├─Base simple functions
40-
-0208 ├─Get username for Ubuntu
41-
-0211 ├─Fix cache dir (if missing)
42-
-0214 ├─Auto-update
43-
-0259 ├─Configuration
44-
-0330 ├─Command generation
45-
-0333 ├─Logging
46-
-0420 ├─Minecraft server utilities
47-
-0517 ├─ChatCommands
48-
-0867 ├─Indev AntiCheat (not currently in development)
49-
-0885 ├─Minecraft server handling
50-
-0892 ├─Keyboard input handler
51-
-0911 ├─Things that run on startup
52-
-1022 ├─Main
53-
-1025 └╢END OF FILE
37+
-0069 ├─Fix main RunServerDotPy directory if missing
38+
-0072 ├─Setup some module-related things
39+
-0075 ├─Setup some values
40+
-0079 ├─Setup profanity filter
41+
-0086 ├─Base simple functions
42+
-0209 ├─Get username for Ubuntu
43+
-0212 ├─Fix cache dir (if missing)
44+
-0215 ├─Auto-update
45+
-0260 ├─Configuration
46+
-0331 ├─Command generation
47+
-0334 ├─Logging
48+
-0421 ├─Minecraft server utilities
49+
-0518 ├─ChatCommands
50+
-0936 ├─Indev AntiCheat (not currently in development)
51+
-0954 ├─Minecraft server handling
52+
-0961 ├─Keyboard input handler
53+
-0980 ├─Things that run on startup
54+
-1090 ├─Main
55+
-1093 └╢END OF FILE

RunServer.py

Lines changed: 76 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
logDir = srvrFolder+'RunServerDotPy/Logs/' # ./RunServerDotPy/Logs/
6565
totalLogDir = logDir+'Total/' # ./RunServerDotPy/Logs/Total/
6666
cacheDir = srvrFolder+'RunServerDotPy/Cache/' # ./RunServerDotPy/Cache/
67+
modDir = srvrFolder+'RunServerDotPy/Mods/' # ./RunServerDotPy/Mods/
6768

6869
# Fix main RunServerDotPy directory if missing
6970
if not os.path.exists(srvrFolder+'RunServerDotPy'): os.mkdir(srvrFolder+'RunServerDotPy')
@@ -461,7 +462,7 @@ def parseOutput(line): #Parses the output, running subfunctions for logging and
461462
logData('"'+chatL[1]+'" tried to run "'+chatL[2]+'"', 'ChatCommands')
462463
parseChatCommand(chatL[1], chatL[2])
463464
elif chatL[0] == 2: logData('"'+chatL[1]+'" /me\'d "'+chatL[2]+'"', 'Chat') #Is a /me message, save it in the chat log formatted like: "[User]" /me'd "[Message]"
464-
elif chatL[0] == 3: logData('"'+chatL[1]+'" /said "'+chatL[2]+'"', 'Chat') #Is a /say message, save it in the chat log formatted like: "[User]" /said "[Message]"
465+
elif (chatL[0] == 3) and (not chatL[1] == 'Server'): logData('"'+chatL[1]+'" /said "'+chatL[2]+'"', 'Chat') #Is a /say message, save it in the chat log formatted like: "[User]" /said "[Message]"
465466
if profanity.contains_profanity(chatL[1]): #Chat message potentially contains profanity, warn user and log it
466467
tellRaw('<'+chatL[0]+'> '+profanity.censor(chatL[1], '!'))
467468
tellRaw('Potential profanity detected and logged', '[Swore!]')
@@ -545,8 +546,10 @@ def updateHelp():
545546
f.write(getFileFromServer('https://raw.githubusercontent.com/Tiger-Tom/RunServerDotPy-extras/main/Help/Version'))
546547
with open(cacheDir+'help/ChatCommandsHelp.json') as f:
547548
chatCommandsHelp,chatCommandsHelpAdmin,chatCommandsHelpRoot = json.load(f) #Load ChatCommands help from cached JSON file
549+
updateHelp()
550+
548551
# Remove unusable help strings
549-
if os.name == 'nt': del chatCommandsHelpAdmin['update | upgrade {dist,clean,pip}'] #The necessary commands to update/upgrade are not on Windows, so delete the help string
552+
if os.name == 'nt': del chatCommandsHelpAdmin['update | upgrade {dist,clean}'] #The necessary commands to update/upgrade are not on Windows, so delete the help string
550553

551554
# Configuration variables
552555
sysInfoShown = {
@@ -602,11 +605,11 @@ def cc_help(ccHelpDict, args, ccPref=chatComPrefix, user='', toConsole=False): #
602605
for i in ccHelpDict.keys():
603606
if showAll:
604607
if toConsole: print (' • '+i)
605-
else: writeCommand('tellraw '+user+(' [" • ",{"text":"{%COMMAND_FULL}","clickEvent":{"action":"suggest_command","value":"{%COMMAND_SUGGEST}"},"hoverEvent":{"action":"show_text","contents":[{"text":"{%COMMAND_SUGGEST}","bold":true}]}}]').replace('{%COMMAND_FULL}', i).replace('{%COMMAND_SUGGEST}', ccPref+i.split(' ')[0]))
608+
else: writeCommand('tellraw '+user+(' [" • ",{"text":"{%COMMAND_FULL}","clickEvent":{"action":"suggest_command","value":"{%COMMAND_SUGGEST}"},"hoverEvent":{"action":"show_text","contents":[{"text":"{%COMMAND_SUGGEST}","bold":true}]}}]').replace('{%COMMAND_FULL}', safeTellRaw(i)).replace('{%COMMAND_SUGGEST}', safeTellRaw(ccPref+i.split(' ')[0])))
606609
else:
607610
if command in i:
608611
if toConsole: print (' • '+i+': '+ccHelpDict[i])
609-
else: writeCommand('tellraw '+user+(' [" • ",{"text":"{%COMMAND_FULL}","clickEvent":{"action":"suggest_command","value":"{%COMMAND_SUGGEST}"},"hoverEvent":{"action":"show_text","contents":[{"text":"{%COMMAND_SUGGEST}","bold":true}]}},{"text":": {%COMMAND_DESC}","hoverEvent":{"action":"show_text","contents":[{"text":"{%COMMAND_DESC}","bold":true}]}}]').replace('{%COMMAND_FULL}', i).replace('{%COMMAND_SUGGEST}', ccPref+i.split(' ')[0]).replace('{%COMMAND_DESC}', ccHelpDict[i]))
612+
else: writeCommand('tellraw '+user+(' [" • ",{"text":"{%COMMAND_FULL}","clickEvent":{"action":"suggest_command","value":"{%COMMAND_SUGGEST}"},"hoverEvent":{"action":"show_text","contents":[{"text":"{%COMMAND_SUGGEST}","bold":true}]}},{"text":": {%COMMAND_DESC}","hoverEvent":{"action":"show_text","contents":[{"text":"{%COMMAND_DESC}","bold":true}]}}]').replace('{%COMMAND_FULL}', safeTellRaw(i)).replace('{%COMMAND_SUGGEST}', safeTellRaw(ccPref+i.split(' ')[0])).replace('{%COMMAND_DESC}', safeTellRaw(ccHelpDict[i])))
610613
def cc_parseSpeedTest(res):
611614
res = json.loads(res)
612615
down = makeValues(res['download'], 1024, sizeVals)
@@ -652,10 +655,74 @@ def cc_parseTagFlag(flag, user):
652655
else:
653656
print ('% ERROR: UNKNOWN FLAG "'+str(flag)+'"')
654657

658+
# Load ChatCommand mods
659+
modsRegular = {}
660+
modsAdmin = {}
661+
modsRoot = {}
662+
modded = False
663+
def loadMods():
664+
if not os.path.exists(modDir): #Check if the mod directory exists and create it if it doesn't
665+
print ('Mods dir doesn\'t exist, creating one now...')
666+
os.mkdir(modDir)
667+
modFiles = [f for f in os.listdir(modDir) if os.path.isfile(modDir+'/'+f)] #Find all file in the mod directory
668+
if not len(modFiles): #If no files/mods were found
669+
print ('No mods found')
670+
return False
671+
mods = []
672+
problems = []
673+
print ('Loading '+str(len(modFiles))+' mod file(s)...')
674+
for i in modFiles: #Load all mods
675+
try:
676+
with open(modDir+'/'+i) as f:
677+
print ('Loading mod file '+i+'...')
678+
exec(f.read()) #Read & parse mod
679+
global mod_output_
680+
mod = mod_output_
681+
del mod_output_
682+
print ('Checking mod...')
683+
if len(mod) != 3: #Check if the mod returns 3 arguments
684+
raise IndexError('Mod doesn\'t return the correct amount of arguments (required: 3, got: '+str(len(mod))+')')
685+
if (type(mod[0]) != dict) or (type(mod[1]) != dict) or (type(mod[2]) != int): #Check to make sure that the mod returns the correct type of arguments
686+
raise TypeError('Mod doesn\t return the proper argument types')
687+
if (mod[2] < 0) or (mod[2] > 2): #Check if the mod's permission level is within the proper bounds
688+
raise ValueError('Mod doesn\'t returnn the proper permission level (expected: 0-2, got: '+str(mod[2])+')')
689+
mods.append(mod) #Everything above didn't run, so the mod should be good
690+
print ('Finished loading mod file '+i)
691+
except Exception as e:
692+
print ('Couldn\'t load mod file '+i+' because:\n'+str(e))
693+
problems.append((i, e))
694+
print ('Finished loading '+str(len(modFiles))+' mod file(s) ('+str(len(mods))+'/'+str(len(modFiles))+' passed checking)')
695+
if not len(mods): #If mod files were found, but they were all invalid
696+
print ('No valid mods found')
697+
return False
698+
global chatCommandsHelp,chatCommandsHelpAdmin,chatCommandsHelpRoot,modsRegular,modsAdmin,modsRoot
699+
print ('Applying '+str(len(mods))+' mod(s)...')
700+
for indx,mod in enumerate(mods):
701+
print ('Applying mod '+str(indx+1)+'/'+str(len(mods))+'...')
702+
if mod[2] == 0: #Mod functions have "regular" permission level
703+
modsRegular.update(mod[1])
704+
chatCommandsHelp.update(mod[0])
705+
elif mod[2] == 1: #Mod functions have "admin" permission level (requires sudo)
706+
modsAdmin.update(mod[1])
707+
chatCommandsHelpAdmin.update(mod[0])
708+
elif mod[2] == 2: #Mod functions have "root" permission level (requires root)
709+
modsRoot.update(mod[1])
710+
chatCommandsHelpRoot.update(mod[0])
711+
print ('Applied mod '+str(indx+1)+'/'+str(len(mods))+'\n- Permission level: '+str(mod[2])+'\n- '+str(len(mod[1]))+' new/modified ChatCommands applied\n- '+str(len(mod[0]))+' new/modified help strings applied')
712+
print ('Successfully applied '+str(len(mods))+' mod(s)')
713+
if len(problems): #Show all problematic mods
714+
print ('\n'+str(len(problems))+' mod(s) failed to load:')
715+
for i,j in problems:
716+
print ('Failed to load mod file '+str(i)+' because '+str(j))
717+
print ()
718+
return True
719+
loadMods()
720+
655721
# Basic functions (any user can run)
656722
def runChatCommand(cmd, args, user):
657723
tellRaw('Running ChatCommand '+cmd, '$ '+user)
658-
if cmd == 'help': cc_help(chatCommandsHelp, args, user=user) #Displays a list of commands, or details for a specific command if it is specified in the arguments
724+
if len(modsRegular) and (cmd in modsRegular): modsRegular[cmd](args, user) #Check if there are any mod commands loaded, and check the command against any mod commands if they are loaded
725+
elif cmd == 'help': cc_help(chatCommandsHelp, args, user=user) #Displays a list of commands, or details for a specific command if it is specified in the arguments
659726
elif (cmd == 'emoticons') and (user != ccRootUsr): threading.Thread(target=cc_tellrawEmoticons, args=(user,args), daemon=True).start() #Shows a list of copyable emoticons, which are configurable by the server owner
660727
elif (cmd == 'nuke') and (user != ccRootUsr): cc_crashPlayer(user, 1) #Secret command
661728
elif cmd == 'size': #Displays the size of the server's world folder
@@ -727,7 +794,8 @@ def runChatCommand(cmd, args, user):
727794
# Sudo/admin commands (only admins or server console can run these commands)
728795
def runAdminChatCommand(cmd, args, user):
729796
tellRaw('Running SuperUser ChatCommand '+cmd, '$'+user, user)
730-
if cmd == 'help': cc_help(chatCommandsHelpAdmin, args, chatComPrefix+'sudo', user) #Display a list of admin commands, or information on a specific one if specified in arguments
797+
if len(modsAdmin) and (cmd in modsAdmin): modsAdmin[cmd](args, user) #Check if there are any mod commands loaded, and check the command against any mod commands if they are loaded
798+
elif cmd == 'help': cc_help(chatCommandsHelpAdmin, args, chatComPrefix+'sudo', user) #Display a list of admin commands, or information on a specific one if specified in arguments
731799
elif cmd in {'antimalware', 'antivirus', 'scan'}:
732800
tellRaw('Operating system: '+os.name, 'AntiMal')
733801
if os.name == 'nt':
@@ -845,7 +913,8 @@ def runAdminChatCommand(cmd, args, user):
845913
# Root commands (only server console can run these commands)
846914
def runRootChatCommand(cmd, args):
847915
print ('$ Running Root ChatCommand '+cmd)
848-
if cmd == 'help': cc_help(chatCommandsHelpRoot, args, chatComPrefix+'root', '', True)
916+
if len(modsRoot) and (cmd in modsRoot): modsRoot[cmd](args, user) #Check if there are any mod commands loaded, and check the command against any mod commands if they are loaded
917+
elif cmd == 'help': cc_help(chatCommandsHelpRoot, args, chatComPrefix+'root', '', True)
849918
elif cmd == 'clearlogs':
850919
global loggedAmountIter, loggedAmountTotal
851920
print ('Finishing logs so that they can be safely removed...')
@@ -954,7 +1023,6 @@ def serverHandler(): #Does everything that is needed to start the server and saf
9541023
#Setup functions
9551024
keyboard.unhook_all() #Unhook keyboard while processes are running
9561025
autoBackup() #Automatically backup everything
957-
updateHelp() #Update the help cache file
9581026
swapIcon() #Swap the server icon (if there are any present)
9591027
swapMOTD(uptimeStart) #Swap the server MOTD (message of the day) (if there are any present)
9601028
makeLogFile() #Setup logging

0 commit comments

Comments
 (0)