44VERSION_MANIFEST = "https://piston-meta.mojang.com/mc/game/version_manifest_v2.json"
55BUNGEECORD_DOWNLOAD_URL = "https://ci.md-5.net/job/BungeeCord/lastStableBuild/artifact/bootstrap/target/BungeeCord.jar"
66APP_VERSION = 1 #The API Version.
7- APP_UF_VERSION = "1.50.2 "
7+ APP_UF_VERSION = "1.50.3 "
88#The semver version
99UPDATEINSTALLED = False
1010DOCFILE = "https://github.com/Enderbyte-Programs/CraftServerSetup/raw/main/doc/craftserversetup.epdoc"
@@ -2917,6 +2917,7 @@ def __init__(self,lowerdata:LogEntry):
29172917class ServerMinuteFrame :
29182918 minuteid :int
29192919 onlineplayers :list [str ] = []
2920+ playerminutes :int = None
29202921
29212922 def __init__ (self ,minuteid ):
29222923 self .minuteid = minuteid
@@ -2926,6 +2927,11 @@ def todatetime(self) -> datetime:
29262927 return datetime .datetime .fromtimestamp (self .minuteid * 60 )
29272928 def howmanyonline (self ) -> int :
29282929 return len (self .onlineplayers )
2930+ def getplayerminutes (self ) -> int :
2931+ if self .playerminutes is None :
2932+ return self .howmanyonline ()
2933+ else :
2934+ return self .playerminutes
29292935
29302936def serverminuteframe_uf (smf :ServerMinuteFrame ):
29312937 return f"{ smf .minuteid } ({ smf .todatetime ()} ) - { smf .onlineplayers } "
@@ -2950,27 +2956,46 @@ def split_list_into_chunks(l, n):
29502956 yield l [i :i + n ]
29512957
29522958class AnalyticsExplorerZoomLevels (enum .Enum ):
2953- MINUTE = 0
2959+ MINUTE = 1
29542960 HOUR = 60
29552961 DAY = 1440
2956- MONTH = 43200
2962+ WEEK = 1440 * 7
29572963
29582964class AnalyticsExplorerDataTypes (enum .Enum ):
29592965 TOTALPLAYERMINUTES = 0
2960- MAXPLAYERCOUNT = 1
2961- AVERAGEPLAYERCOUNT = 2
2966+ PLAYERCOUNT = 1
29622967
29632968def sort_dict_by_key (d :dict ) -> dict :
29642969 return dict (sorted (list (d .items ())))
29652970
2971+ def get_chunk_size_from_aezl (s :AnalyticsExplorerZoomLevels ):
2972+ return {
2973+ AnalyticsExplorerZoomLevels .MINUTE :1 ,
2974+ AnalyticsExplorerZoomLevels .HOUR :60 ,
2975+ AnalyticsExplorerZoomLevels .DAY :1440 ,
2976+ AnalyticsExplorerZoomLevels .WEEK :1440 * 7
2977+ }[s ]
2978+
29662979def server_analytics_explorer (stdscr ,data :dict [int ,ServerMinuteFrame ]):
2967-
2980+ cursesplus . displaymsg ( stdscr ,[ "Analytics Explorer" , "Initializing..." ], False )
29682981 #This function allows users to explore their analytics
29692982 offset = 0
29702983 datasize = len (data )- 1
29712984 currentzoomlevel = AnalyticsExplorerZoomLevels .MINUTE #Also passively the list chunk size
2972- currentdatatype = AnalyticsExplorerDataTypes .MAXPLAYERCOUNT
2985+ currentdatatype = AnalyticsExplorerDataTypes .PLAYERCOUNT
2986+ zoomlevels = {
2987+ AnalyticsExplorerZoomLevels .MINUTE : "Minute (default)" ,
2988+ AnalyticsExplorerZoomLevels .HOUR : "Hour" ,
2989+ AnalyticsExplorerZoomLevels .DAY : "Day" ,
2990+ AnalyticsExplorerZoomLevels .WEEK : "Week"
2991+ }
2992+ datatypes = {
2993+ AnalyticsExplorerDataTypes .TOTALPLAYERMINUTES : "Player Minutes Spent" ,
2994+ AnalyticsExplorerDataTypes .PLAYERCOUNT : "Online Players (default)"
2995+ }
29732996 ldata = list (data .values ())#List representation of data to prevent performance issues?
2997+ ogdata = copy .deepcopy (data )
2998+ ogldata = copy .deepcopy (ldata )#For use later
29742999 maxval = max ([len (p .onlineplayers ) for p in list (data .values ())])
29753000 while True :
29763001 my ,mx = stdscr .getmaxyx ()
@@ -2980,8 +3005,6 @@ def server_analytics_explorer(stdscr,data:dict[int,ServerMinuteFrame]):
29803005 cursesplus .utils .fill_line (stdscr ,0 ,cursesplus .set_colour (cursesplus .BLUE ,cursesplus .BLUE ))
29813006 stdscr .addstr (0 ,0 ,"Analytics Explorer - Press Q to quit and H for help" ,cursesplus .set_colour (cursesplus .BLUE ,cursesplus .WHITE ))
29823007
2983- if currentdatatype == AnalyticsExplorerDataTypes .TOTALPLAYERMINUTES :
2984- maxval = maxval * currentzoomlevel
29853008
29863009 ti = 0
29873010 for i in range (offset - xspace // 2 ,offset + xspace // 2 ):
@@ -2992,7 +3015,10 @@ def server_analytics_explorer(stdscr,data:dict[int,ServerMinuteFrame]):
29923015 stdscr .addstr (dy ,ti ,"█" ,cursesplus .set_colour (cursesplus .RED ,cursesplus .RED ))
29933016 else :
29943017 seldata = ldata [i ]
2995- scale = int (seldata .howmanyonline ()/ maxval * yspace )
3018+ if currentdatatype == AnalyticsExplorerDataTypes .PLAYERCOUNT :
3019+ scale = int (seldata .howmanyonline ()/ maxval * yspace )
3020+ else :
3021+ scale = int (seldata .getplayerminutes ()/ maxval * yspace )
29963022
29973023 if i == offset :
29983024 for p in range (1 ,yspace + 2 ):
@@ -3006,13 +3032,19 @@ def server_analytics_explorer(stdscr,data:dict[int,ServerMinuteFrame]):
30063032 ti += 1
30073033
30083034 seldata = ldata [offset ]
3009- stdscr .addstr (my - 2 ,0 ,f"{ strip_datetime (get_datetime_from_minute_id (seldata .minuteid ))} || { seldata .howmanyonline ()} players online - { seldata .onlineplayers } " )
3035+ if currentdatatype == AnalyticsExplorerDataTypes .TOTALPLAYERMINUTES :
3036+ stdscr .addstr (my - 2 ,0 ,f"{ strip_datetime (get_datetime_from_minute_id (seldata .minuteid ))} || { seldata .getplayerminutes ()} player-minutes" )
3037+ else :
3038+ try :
3039+ stdscr .addstr (my - 2 ,0 ,f"{ strip_datetime (get_datetime_from_minute_id (seldata .minuteid ))} || { seldata .howmanyonline ()} players online - { seldata .onlineplayers } " )
3040+ except :
3041+ stdscr .addstr (my - 2 ,0 ,f"{ strip_datetime (get_datetime_from_minute_id (seldata .minuteid ))} || { seldata .howmanyonline ()} players online" )#Too long for list
30103042
30113043 ch = curses .keyname (stdscr .getch ()).decode ()
30123044 if ch == "q" :
30133045 break
30143046 elif ch == "h" :
3015- cursesplus .displaymsg (stdscr ,["KEYBINDS" ,"q - Quit" ,"h - Help" ,"<- -> Scroll" ,"END - Go to end" ,"HOME - Go to beginning" ,"j - Jump to time" ,"SHIFT <-- --> - Jump hour" ,"Ctrl <-- --> - Jump day" ])
3047+ cursesplus .displaymsg (stdscr ,["KEYBINDS" ,"q - Quit" ,"h - Help" ,"<- -> Scroll" ,"END - Go to end" ,"HOME - Go to beginning" ,"j - Jump to time" ,"SHIFT <-- --> - Jump hour" ,"Ctrl <-- --> - Jump day" , "T - Select time unit" , "D - Select data type" ])
30163048 elif ch == "KEY_LEFT" :
30173049 if offset > 0 :
30183050 offset -= 1
@@ -3025,9 +3057,9 @@ def server_analytics_explorer(stdscr,data:dict[int,ServerMinuteFrame]):
30253057 offset = 0
30263058 elif ch == "KEY_SRIGHT" :#Jump around by an hour
30273059 offset += 60
3028- elif ch == "kRIT5" :
3060+ elif ch == "kRIT5" :#ctrl left
30293061 offset += 1440
3030- elif ch == "kLFT5" :
3062+ elif ch == "kLFT5" :#ctrl right
30313063 if offset > 1440 :
30323064 offset -= 1440
30333065 else :
@@ -3039,11 +3071,52 @@ def server_analytics_explorer(stdscr,data:dict[int,ServerMinuteFrame]):
30393071 elif ch == "j" :
30403072 stdscr .clear ()
30413073 ndate = cursesplus .date_time_selector (stdscr ,cursesplus .DateTimeSelectorTypes .DATEANDTIME ,"Choose a date and time to jump to" ,True ,False ,get_datetime_from_minute_id (ldata [offset ].minuteid ))
3074+ if currentzoomlevel == AnalyticsExplorerZoomLevels .HOUR :
3075+ ndate .minute = 0
3076+ ndate .second = 0
3077+ if currentzoomlevel == AnalyticsExplorerZoomLevels .DAY or currentzoomlevel == AnalyticsExplorerZoomLevels .WEEK :
3078+ ndate .hour = 0
30423079 nmid = get_minute_id_from_datetime (ndate )
30433080 if not nmid in data :
30443081 cursesplus .messagebox .showerror (stdscr ,["Records do not exist for the selected date." ])
30453082 else :
30463083 offset = list (data .keys ()).index (nmid )
3084+ elif ch == "t" :
3085+ currentzoomlevel = list (zoomlevels .keys ())[crss_custom_ad_menu (stdscr ,list (zoomlevels .values ()),"Choose a zoom level" )]
3086+ cursesplus .displaymsg (stdscr ,["Generating Data" ],False )
3087+ #This will use ogdat because data may have been used for hour or day, which will screw it up
3088+ offset = 0
3089+ if currentzoomlevel == AnalyticsExplorerZoomLevels .MINUTE :
3090+ data = ogdata
3091+ ldata = ogldata
3092+ else :
3093+ chunked_data :list [list [ServerMinuteFrame ]] = split_list_into_chunks (ogldata ,get_chunk_size_from_aezl (currentzoomlevel ))
3094+ final_data :dict [int ,ServerMinuteFrame ] = {}
3095+ for chunk in chunked_data :
3096+ s :ServerMinuteFrame = ServerMinuteFrame (chunk [0 ].minuteid )
3097+ #Append all unique players, and set playerminutes
3098+ total_playerminutes = 0
3099+ unique_players = []
3100+ for minute in chunk :
3101+ total_playerminutes += minute .howmanyonline ()
3102+ for onlineplayer in minute .onlineplayers :
3103+ if onlineplayer not in unique_players :
3104+ unique_players .append (onlineplayer )
3105+ s .playerminutes = total_playerminutes
3106+ s .onlineplayers = unique_players
3107+ final_data [s .minuteid ] = s
3108+ data = final_data
3109+ ldata = list (data .values ())
3110+ maxval = max ([p .getplayerminutes () for p in list (data .values ())])
3111+ #if currentdatatype == AnalyticsExplorerDataTypes.TOTALPLAYERMINUTES:
3112+ # maxval = maxval*get_chunk_size_from_aezl(currentzoomlevel)
3113+ datasize = len (data )- 1
3114+
3115+ elif ch == "d" :
3116+ currentdatatype = list (datatypes .keys ())[crss_custom_ad_menu (stdscr ,list (datatypes .values ()),"Choose a data type" )]
3117+ maxval = max ([p .getplayerminutes () for p in list (data .values ())])
3118+ #if currentdatatype == AnalyticsExplorerDataTypes.TOTALPLAYERMINUTES:
3119+ # maxval = maxval*get_chunk_size_from_aezl(currentzoomlevel)
30473120 if offset > datasize :
30483121 offset = datasize - 1
30493122
@@ -3082,7 +3155,8 @@ def sanalytics(stdscr,serverdir):
30823155 rs :str = lz [0 ].strip ()
30833156 plname = rs .split (" " )[0 ]
30843157 action = rs .split (" " )[1 ]
3085-
3158+ #Remove special characters from plname
3159+ plname = plname .replace ("(" ,"" )
30863160 mid = get_minute_id_from_datetime (eventslist [- 1 ].ext )
30873161 if "joined" in action :
30883162 if not mid in joins :
0 commit comments