import re from database import ( getDMGFPlaylogbyToken, getDMGFProfileDetailsbyToken, getDMGFProfileExtbyToken, getEacnetProfilebyToken, getDMGFProfilebyToken, add_user_DMGF, manage_user_DMGF, manage_user_DMGF_Details, update_DMGF_playlog, ) from variables.strings.games.gitadora import ( gitadoraNewPlayerData, gitadoraPlayerData, gitadoraRegisterData, gitadoraMusicDB, gitadoraFullPlayerData, gitadoraGameStatus, gitadoraGameEnd, gitadoraStageResult, gitadoraMusicItem, customDataDefault, favMusicDefault ) from variables.strings.common import genericError from variables.protocols import EAC_XML, EAC_RESULT from time import time from random import choices import xmltodict def clamp_u64(value): """Clamp value to valid u64 range""" try: val = int(float(value)) return max(0, min(val, 18446744073709551615)) except: return 0 def knst_cardutil_return(playStyle, userProfile, playerIdentification): skill = 0 allSkill = 0 if playStyle == "GF": skill = userProfile.GFskill allSkill = userProfile.GFallSkill elif playStyle == "DM": skill = userProfile.DMskill allSkill = userProfile.DMallSkill return gitadoraPlayerData.format(refid=playerIdentification.refid,snsid=playerIdentification.snsid,usercode=userProfile.usercode,skill=skill,allSkill=allSkill).encode() def gitadoraRelay(module, method, data, playerToken): print(f"{module} {method}") playerIdentification = getEacnetProfilebyToken(playerToken) playStyle = data.get("gametype", {}).get("#text", "U32:J:A") # Default to GF if playstyle is not avalible match playStyle: case "U32:J:A": playStyle = "GF" case "U32:J:B": playStyle = "DM" match module: case "knst_playablemusic": match method: case "get": return gitadoraMusicDB case "knst_gameinfo": match method: case "get": return gitadoraGameStatus case "knst_cardutil": match method: case "check": userProfile = getDMGFProfilebyToken(playerToken) if userProfile is None: return gitadoraNewPlayerData return knst_cardutil_return(playStyle,userProfile,playerIdentification) case "regist": newUserCode = ''.join(choices('0123456789ABCDEF', k=10)) userProfile = getDMGFProfilebyToken(playerToken) userProfileExt = getDMGFProfileExtbyToken(playerToken) userProfileDetails = getDMGFProfileDetailsbyToken(playerToken) if userProfile is None: manage_user_DMGF_Details(playerIdentification.refid,playerToken,[],"") manage_user_DMGF(playerIdentification.refid,playerToken,"","","","") newUser = add_user_DMGF(playerIdentification.refid,playerToken,playerIdentification.snsid,newUserCode) if newUser is not None: return gitadoraRegisterData.format(refid=playerIdentification.refid,snsid=playerIdentification.snsid,usercode=newUserCode).encode() else: print("throwing generic error") return genericError.format(protocol=EAC_XML,result=EAC_RESULT) # Something went wrong with the user Registration, and it needs to start over again. else: print("Registering new card for user ") return gitadoraRegisterData.format(refid=playerIdentification.refid,snsid=playerIdentification.snsid,usercode=newUserCode).encode() case "knst_gametop": match method: case "get": userProfile = getDMGFProfilebyToken(playerToken) userProfileExt = getDMGFProfileExtbyToken(playerToken) userProfileDetails = getDMGFProfileDetailsbyToken(playerToken) stageResult = '' musiclist = '' playlogs = getDMGFPlaylogbyToken(playerToken, playStyle) playstyle = customDataDefault custom = customDataDefault lastCategory = 0 lastMusicID = -1 lastSeq = 0 dispLevel = 0 cabid = 0 play = 0 playtime = 0 playterm = 0 session_cnt = 0 matching_num = 0 extra_stage = 0 extra_play = 0 extra_clear = 0 encore_play = 0 encore_clear = 0 pencore_play = 0 pencore_clear = 0 max_clear_diff = 0 max_full_diff = 0 max_exce_diff = 0 clear_num = 0 full_num = 0 exce_num = 0 no_num = 0 e_num = 0 d_num = 0 c_num = 0 b_num = 0 a_num = 0 s_num = 0 ss_num = 0 musicFavorites1 = favMusicDefault musicFavorites2 = favMusicDefault musicFavorites3 = favMusicDefault if playlogs and len(playlogs) > 0: gitadoraMusicDBdict = xmltodict.parse(gitadoraMusicDB.decode()) # Generate stage results musicItems = {} # Dictionary to store music data grouped by musicid # Group playlogs by musicid for playlog in playlogs: if playlog.musicid not in musicItems: musicItems[playlog.musicid] = { 'musicid': playlog.musicid, 'sequences': { 1: None, # Guitar Basic 2: None, # Guitar Advanced 3: None, # Guitar Extreme 4: None, # Guitar Master 5: None, # Bass Basic 6: None, # Bass Advanced 7: None, # Bass Extreme 8: None # Bass Master } } # Get current data for this sequence current_data = musicItems[playlog.musicid]['sequences'][playlog.seq] # Determine if we should replace the existing data should_replace = False if current_data is None: should_replace = True else: # Check importance ladder if playlog.excellent: # Replace if current isn't excellent or if both excellent but new perc is higher if not current_data['excellent'] or (current_data['excellent'] and playlog.perc > current_data['perc']): should_replace = True elif playlog.fullcombo: # Replace if current isn't excellent and isn't fullcombo if not current_data['excellent'] and not current_data['fullcombo']: should_replace = True elif playlog.clear: # Replace if current isn't excellent, isn't fullcombo, and isn't clear if not current_data['excellent'] and not current_data['fullcombo'] and not current_data['clear']: should_replace = True # Only update if we should replace if should_replace: musicItems[playlog.musicid]['sequences'][playlog.seq] = { 'stage_no': playlog.stage_no, 'skill': playlog.skill, 'new_skill': playlog.new_skill, 'clear': playlog.clear, 'auto_clear': playlog.auto_clear, 'fullcombo': playlog.fullcombo, 'excellent': playlog.excellent, 'medal': playlog.medal, 'perc': playlog.perc, 'new_perc': playlog.new_perc, 'rank': playlog.rank, 'score': playlog.score, 'combo': playlog.combo, 'max_combo_perc': playlog.max_combo_perc, 'flags': playlog.flags, 'phrase_combo_perc': playlog.phrase_combo_perc, 'meter': playlog.meter, 'meter_prog': playlog.meter_prog } musiclistPrep = "" musiclistLength = 0 for musicid, data in musicItems.items(): print(musicid) flagFC = 0 flagEX = 0 flagCLR = 0 # Initialize arrays with correct length and default values meter = ["0"]*8 # Array of 8 zeros as strings meterProg = [0]*8 # Array of 8 zeros mdata = [-1] + [-1]*14 + [0]*2 # 17 elements total sdataSeq = -1 sdataSkill = -1 best_skill = -1 # Track the best skill for seq, seqData in data["sequences"].items(): try: seq = int(seq) print(f"Processing sequence {seq} with data:", seqData) if seqData is not None: # Update skill data if this sequence has a higher skill if seqData["skill"] > best_skill: best_skill = seqData["skill"] sdataSeq = seq sdataSkill = seqData["skill"] flagFC += seqData["fullcombo"] * 2**seq flagEX += seqData["excellent"] * 2**seq flagCLR += seqData["clear"] * 2**seq # Safely handle meter values if seqData["meter"]: try: # Clamp meter value to valid u64 range meter[seq-1] = str(clamp_u64(seqData["meter"])) except Exception as e: print(f"Error processing meter for seq {seq}: {e}") meter[seq-1] = "0" try: meterProg[seq-1] = int(seqData["meter_prog"]) except (ValueError, TypeError): print(f"Invalid meter_prog value for seq {seq}: {seqData['meter_prog']}") meterProg[seq-1] = 0 # Only set perc/rank if the chart was cleared if seqData["clear"]: try: mdata[seq] = int(seqData.get("perc", -2)) mdata[seq+8] = int(seqData.get("rank", 0)) except (ValueError, TypeError) as e: print(f"Error processing perc/rank for seq {seq}: {e}") mdata[seq] = -2 mdata[seq+8] = 0 except Exception as e: print(f"Error processing sequence {seq}: {e}") continue # Format the music item musiclistPrep += gitadoraMusicItem.format( musicId=musicid, flag=f"{flagFC} {flagEX} {flagCLR} 0", sdata=f"{sdataSeq} {sdataSkill}", mdata=" ".join(str(x) for x in mdata), meter=" ".join(meter), meterProg=" ".join(str(x) for x in meterProg) ) musiclistLength += 1 musiclist = f'{musiclistPrep}' # constructing the stageResults stageResults = "" for playlog in playlogs: # Find the musicid in the gitadoraMusicDBdict # Find the matching music entry in the musicinfo list music_info = None for music in gitadoraMusicDBdict["eacnet"]["response"]["knst_playablemusic"]["musicinfo"]["music"]: if music["id"]["#text"] == str(playlog.musicid): music_info = music break stageResults += gitadoraStageResult.format( dateMs=playlog.date_ms, stage=playlog.stage_no, skill=playlog.skill, musicId=playlog.musicid, diff=music_info["diff"]["#text"].split()[playlog.seq], # Get difficulty from music_info seqmode=playlog.seq, musicTitle=music_info.get("music_title", {}).get("#text", ""), musicArtist=music_info.get("artist", {}).get("#text", ""), rightsCredits=music_info.get("right_credit", {}).get("#text", ""), clearStatus=playlog.clear, rank=playlog.rank, perfect=playlog.perfect, great=playlog.great, good=playlog.good, ok=playlog.ok, miss=playlog.miss, perfectPerc=playlog.perfect_perc, greatPerc=playlog.great_perc, goodPerc=playlog.good_perc, okPerc=playlog.ok_perc, missPerc=playlog.miss_perc, maxCombo=playlog.combo, maxComboPerc=playlog.max_combo_perc, score=playlog.score, flags=playlog.flags, point=playlog.skill, # Using skill as point newPoint=playlog.new_skill, perc=playlog.perc, newPerc=playlog.new_perc, meter=playlog.meter, meterProg=playlog.meter_prog, beforeMeter=playlog.before_meter, beforeMeterProg=playlog.before_meter_prog, isNewMeter=playlog.is_new_meter, phraseDataNum=playlog.phrase_data_num, phraseAddr=playlog.phrase_addr, phraseType=playlog.phrase_type, phraseStatus=playlog.phrase_status, phraseEndAddr=playlog.phrase_end_addr, phraseComboPerc=playlog.phrase_combo_perc ) if stageResults: stageResult = f"{stageResults}" else: stageResult = "" if playStyle == "GF": playstyle = userProfileExt.GFplaystyle custom = userProfileExt.GFcustom lastCategory = userProfileExt.GFlastCategory lastMusicID = userProfileExt.GFlastMusic lastSeq = userProfileExt.GFlastSeq dispLevel = userProfileExt.GFdisplayLevel cabid = userProfileDetails.GFcabid play = userProfileDetails.GFplay playtime = userProfileDetails.GFplaytime playterm = userProfileDetails.GFplayterm session_cnt = userProfileDetails.GFsession_cnt matching_num = userProfileDetails.GFmatching_num extra_stage = userProfileDetails.GFextra_stage extra_play = userProfileDetails.GFextra_play extra_clear = userProfileDetails.GFextra_clear encore_play = userProfileDetails.GFencore_play encore_clear = userProfileDetails.GFencore_clear pencore_play = userProfileDetails.GFpencore_play pencore_clear = userProfileDetails.GFpencore_clear max_clear_diff = userProfileDetails.GFmax_clear_diff max_full_diff = userProfileDetails.GFmax_full_diff max_exce_diff = userProfileDetails.GFmax_exce_diff clear_num = userProfileDetails.GFclear_num full_num = userProfileDetails.GFfull_num exce_num = userProfileDetails.GFexce_num no_num = userProfileDetails.GFno_num e_num = userProfileDetails.GFe_num d_num = userProfileDetails.GFd_num c_num = userProfileDetails.GFc_num b_num = userProfileDetails.GFb_num a_num = userProfileDetails.GFa_num s_num = userProfileDetails.GFs_num ss_num = userProfileDetails.GFss_num musicFavorites1 = userProfileExt.GFmusicFavorites1 musicFavorites2 = userProfileExt.GFmusicFavorites2 musicFavorites3 = userProfileExt.GFmusicFavorites3 elif playStyle == "DM": playstyle = userProfileExt.DMplaystyle custom = userProfileExt.DMcustom lastCategory = userProfileExt.DMlastCategory lastMusicID = userProfileExt.DMlastMusic lastSeq = userProfileExt.DMlastSeq dispLevel = userProfileExt.DMdisplayLevel cabid = userProfileDetails.DMcabid play = userProfileDetails.DMplay playtime = userProfileDetails.DMplaytime playterm = userProfileDetails.DMplayterm session_cnt = userProfileDetails.DMsession_cnt matching_num = userProfileDetails.DMmatching_num extra_stage = userProfileDetails.DMextra_stage extra_play = userProfileDetails.DMextra_play extra_clear = userProfileDetails.DMextra_clear encore_play = userProfileDetails.DMencore_play encore_clear = userProfileDetails.DMencore_clear pencore_play = userProfileDetails.DMpencore_play pencore_clear = userProfileDetails.DMpencore_clear max_clear_diff = userProfileDetails.DMmax_clear_diff max_full_diff = userProfileDetails.DMmax_full_diff max_exce_diff = userProfileDetails.DMmax_exce_diff clear_num = userProfileDetails.DMclear_num full_num = userProfileDetails.DMfull_num exce_num = userProfileDetails.DMexce_num no_num = userProfileDetails.DMno_num e_num = userProfileDetails.DMe_num d_num = userProfileDetails.DMd_num c_num = userProfileDetails.DMc_num b_num = userProfileDetails.DMb_num a_num = userProfileDetails.DMa_num s_num = userProfileDetails.DMs_num ss_num = userProfileDetails.DMss_num musicFavorites1 = userProfileExt.DMmusicFavorites1 musicFavorites2 = userProfileExt.DMmusicFavorites2 musicFavorites3 = userProfileExt.DMmusicFavorites3 return gitadoraFullPlayerData.format( refid=playerIdentification.refid, snsid=playerIdentification.snsid, usercode=userProfile.usercode, time=int(time()), playstyle=playstyle, custom=custom, lastCategory=lastCategory, lastMusicID=lastMusicID, lastSeq=lastSeq, dispLevel=dispLevel, cabid=cabid, play=play, playtime=playtime, playterm=playterm, session_cnt=session_cnt, matching_num=matching_num, extra_stage=extra_stage, extra_play=extra_play, extra_clear=extra_clear, encore_play=encore_play, encore_clear=encore_clear, pencore_play=pencore_play, pencore_clear=pencore_clear, max_clear_diff=max_clear_diff, max_full_diff=max_full_diff, max_exce_diff=max_exce_diff, clear_num=clear_num, full_num=full_num, exce_num=exce_num, no_num=no_num, e_num=e_num, d_num=d_num, c_num=c_num, b_num=b_num, a_num=a_num, s_num=s_num, ss_num=ss_num, musicFavorites1=musicFavorites1, musicFavorites2=musicFavorites2, musicFavorites3=musicFavorites3, stageResult=stageResult, musicList=musiclist ).encode() case "knst_gameend": match method: case "regist": # game is saved here, every time you complete a song playerData = data["player"] customData = playerData["customdata"] skillData = playerData["skilldata"] record = playerData["record"] favoritemusic = playerData["favoritemusic"] playinfo = playerData["playinfo"] stage = playerData.get("stage", {}) manage_user_DMGF(playerIdentification.refid,playerToken,customData,favoritemusic,playinfo,playStyle) manage_user_DMGF_Details(playerIdentification.refid,playerToken,playinfo,playStyle) if stage: update_DMGF_playlog(playerIdentification.refid, playerToken, stage,playStyle) return gitadoraGameEnd case _: # Backup Case return b"NoMatches"