########################################################################################
# This file is part of Spheres.
# Copyright (C) 2019 Jesusalva
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
########################################################################################
# Player Module (parties, inventory, and player data)
from utils import (stdout, dl_search, allunits, date_from_sql, date_from_now,
now, compress, Player, ApTimer)
from consts import (SQL_DELAY, SQL_SAVE_TIME, MAX_INV_SIZE, AP_REGEN_TIME_F,
Job_Swordsman, Job_Mage, Job_Assassin, Job_Archer, Job_Gunner, SQL_CLEAR,
ERR_LOGIN, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY,
ERR_BAD, ERR_OFF, ERR_ERR, CLIENTVERSION, UNSELLABLE, DOUBLE_GP, EXP_UP,
ERR_DONE, ERR_OK, MAX_PARTIES, NO_PARTY, SUPEREVOMAT, EVO_MAT, AP_REGEN_TIME)
import sql
import json, re #,time
from copy import copy
from threading import Timer
####################################### Private methods
# Internal function for ApTimer, DO NOT CAST manually
# FIXME: Maybe find a better replacement for this
def ap_updater(token):
global ApTimer, Player
# TODO: What if token is no longer valid? (Maybe use queue instead?)
Player[token]["ap"]+=1
if (Player[token]["ap"] < Player[token]["max_ap"]):
try:
ApTimer[token].cancel()
del ApTimer[token]
except:
pass
ApTimer[token]=Timer(AP_REGEN_TIME_F, ap_updater, args=[token])
ApTimer[token].daemon=True
ApTimer[token].start()
return
# Internal function for SQL, DO NOT CAST manually
def sql_routine():
global Player, SQLTimer
#stdout("Now saving SQL data")
for token in Player:
stdout("[SQL] Now saving token \"%s\"" % token)
stdout(str(Player[token]))
clear(token, SQL_DELAY)
SQLTimer=Timer(SQL_SAVE_TIME, sql_routine)
SQLTimer.daemon=True
SQLTimer.start()
return
# Attempts to place an item. Return inv index, -1 on error
def inventoryplace(token):
# Check for a free slot or return next index
try:
i=Player[token]["inv"].index(None)
except:
i=len(Player[token]["inv"])
# Return -1 if array exceeds the maximum size
if (i > MAX_INV_SIZE):
return -1
else:
return i
# Adds/Deletes AP from a user, manages the ApTimer
def update_ap(token, newval):
global ApTimer, Player
running=False
if (Player[token]["ap"] < Player[token]["max_ap"]):
running=True
# Update AP
# TODO: Do we allow over-the-cap AP?
Player[token]["ap"]+=newval
# Handle the timer
if (not running and Player[token]["ap"] < Player[token]["max_ap"]):
ApTimer[token]=Timer(AP_REGEN_TIME_F, ap_updater, args=[token])
ApTimer[token].daemon=True
ApTimer[token].start()
if (running and Player[token]["ap"] >= Player[token]["max_ap"]):
# If it is not running/error: We don't care (very rare error)
try:
ApTimer[token].cancel()
except:
pass
return
# Function which list all unit id on party
def party_dupcheck(token, pid):
ar=[]
for unt in Player[token]["party_%s" % pid]:
ar.append(unt["unit_id"])
return ar
# Function which list all unit id on inventory
def inv_dupcheck(token, unique=True):
ar=[]
for unt in Player[token]["inv"]:
if unt is None:
if unique:
continue
else:
ar.append(0)
else:
if unique:
if unt["unit_id"] in ar:
continue
else:
ar.append(unt["unit_id"])
return ar
# Techinically a battle function, readjusts a value
# Rarity affects directly primary stat. We should distribute 140 points
def readjust_status(rar, job, hp=True):
newv=50
# Imbalanced class
if (job == Job_Swordsman):
if hp:
newv+=30+rar
else:
newv+=10+(rar/2)
elif (job == Job_Mage):
if hp:
newv+=10+(rar/2)
else:
newv+=30+rar
# Balanced class
elif (job == Job_Assassin):
if hp:
newv+=15+(rar/2)
else:
newv+=25+rar
elif (job == Job_Archer):
if hp:
newv+=25+rar
else:
newv+=15+(rar/2)
# All-rounder class, with no steady growth
elif (job == Job_Gunner):
if hp:
newv+=20+(rar/2)
else:
newv+=20+(rar/2)
return newv
def clear(token, mask=SQL_CLEAR):
# This function saves and clears a token
#########################################
try:
# Save inventory data to SQL
sql.save_inv(token, mask)
# Erase inventory if asked
if mask & SQL_CLEAR:
Player[token]["inv"]={}
# Save party data to SQL
sql.save_party(token)
# Erase parties if needed
if mask & SQL_CLEAR:
Player[token]["party_1"]={}
Player[token]["party_2"]={}
Player[token]["party_3"]={}
# Now save Player data to SQL
sql.save_player(token, mask)
# Erase Player Structure if needed
if mask & SQL_CLEAR:
del Player[token]
# Stop the AP regeneration if we're clearing
if mask & SQL_CLEAR:
try:
ApTimer[token].cancel()
del ApTimer[token]
except:
pass
except:
stdout("SQL ERROR: FAILED TO CLEAR TOKEN %s" % token)
if mask & SQL_CLEAR:
del Player[token]
if mask & SQL_CLEAR:
try:
ApTimer[token].cancel()
del ApTimer[token]
except:
pass
return
def exp_to_lvlup(level):
# How much exp to rank up?
nmult=50.0
nmult+=level/35.0
nmult+=level/10.0
nmult+=level/6.0
nmult+=level/2.0
nmult=nmult**1.01
next_exp=int(level*nmult)
#stdout("nmult is %.2f, exp needed: %d" % (nmult, next_exp))
return next_exp
# Check if player ranked up
# Return: {"code": True/False, "ap": <ap_raised>, "next": <exp_needed>}
def check_rank_up(token):
rs={"code": False, "ap": 0}
next_exp=exp_to_lvlup(Player[token]["level"])*2
if Player[token]["exp"] >= next_exp:
# We leveled up! Prepare the bonuses
rs["code"]=True
# AP rules: Every 5 levels: +1 AP (always)
if Player[token]["level"] % 5 == 0:
rs["ap"]+=1
# AP rules: Every 10 levels: +1 AP (always)
if Player[token]["level"] % 10 == 0:
rs["ap"]+=1
# AP rules: Every level before the 20th: +1 AP (always_
if Player[token]["level"] < 20:
rs["ap"]+=1
# Apply the level up
Player[token]["exp"]-=next_exp
Player[token]["level"]+=1
Player[token]["max_ap"]+=rs["ap"]
update_ap(token, Player[token]["max_ap"]-Player[token]["ap"])
return rs
# Conditional EXP attribution
# Doesn't returns anything
def grant_exp(token, inv_id, exp):
# We need some data
un=dl_search(allunits, "unit_id", Player[token]["inv"][inv_id]["unit_id"])
if un == "ERROR":
stdout("WARNING, Invalid unit id on GEXP: %d" % (Player[token]["inv"][inv_id]["unit_id"]))
return
# Max level, do nothing
if Player[token]["inv"][inv_id]["level"] >= un["max_level"]:
return
Player[token]["inv"][inv_id]["exp"]+=exp
return
# Same as check_rank_up but for units
# Returns True/False
def check_level_up(token, inv_id):
global allunits
# Prepare un, the variable from unit database
un=dl_search(allunits, "unit_id", Player[token]["inv"][inv_id]["unit_id"])
if un == "ERROR":
stdout("WARNING, Invalid unit id on CLU: %d" % (Player[token]["inv"][inv_id]["unit_id"]))
return -1
# FIX
if Player[token]["inv"][inv_id]["level"] > un["max_level"]:
stdout("WARNING, Overlevelled unit (Lv %d/%d)" % (Player[token]["inv"][inv_id]["level"], un["max_level"]))
Player[token]["inv"][inv_id]["exp"]=0
Player[token]["inv"][inv_id]["level"]=copy(un["max_level"])
# Max level reached (FIXME why it still gets exp?!)
if Player[token]["inv"][inv_id]["level"] >= un["max_level"]:
return 0
# Calculate exp needed to level up
next_exp=exp_to_lvlup(Player[token]["inv"][inv_id]["level"])
ret=0
while Player[token]["inv"][inv_id]["exp"] >= next_exp:
ret += 1
Player[token]["inv"][inv_id]["exp"]-=next_exp
Player[token]["inv"][inv_id]["level"]+=1
# No overflow!
if Player[token]["inv"][inv_id]["level"] >= un["max_level"]:
Player[token]["inv"][inv_id]["exp"]=0
Player[token]["inv"][inv_id]["level"]=copy(un["max_level"])
break
next_exp=exp_to_lvlup(Player[token]["inv"][inv_id]["level"])
return ret
# Calculates sell price of an unit
# After 10 stars, the sell price overflows 10,000 and gets messy
# So I capped it at 10,000. Further stars will only raise level weight.
def calc_sell_price(rarity, level):
return min(10000, rarity**2*100)+(level*rarity)
def daily_login(token):
# Prepare dates from player last login, and from now
# FIXME: "lastlogin" is updated in cycles
# If players pass midnight connected --> BOOM
# We should fill "lastlogin" at sql.load_player() only, instead of
# auto-updating it
dlcode=copy(ERR_LOGIN)
stdout("Last login: %s" % str(Player[token]["lastlogin"]))
# dlcode mask: (ERR_LOGIN meaning)
# 5 - 0 - 0 - 0
# ID - Monthly - Monthly - Weekly
d,m,y,w,H,M=date_from_sql(str(Player[token]["lastlogin"]))
td,tm,ty,tw,tH,tM=date_from_now()
# We don't really care with this bit of info
del H, M, tH, tM
# It's not the same: A reward is due
if (d!=td or m!=tm or y!=ty):
dlcode+=1000
# TODO: Weekly rewards
if (w != tw) or (w == tw and (d != td or m != tm or y != ty)):
# A weekly reward is due!
dlcode+=tw
if tw == MONDAY:
Player[token]["crystals"]+=50
elif tw == TUESDAY:
Player[token]["crystals"]+=50
elif tw == WEDNESDAY:
Player[token]["crystals"]+=50
elif tw == THURSDAY:
Player[token]["crystals"]+=50
elif tw == FRIDAY:
Player[token]["crystals"]+=50
elif tw == SATURDAY:
Player[token]["crystals"]+=50
elif tw == SUNDAY:
Player[token]["crystals"]+=50
else:
stdout("ERROR ERROR, UNKNOWN DAY OF WEEK %d" % tw)
# TODO: Monthly rewards
# TODO: Streak rewards
# Finally, get rid of this data
del Player[token]["lastlogin"]
return dlcode
####################################### Public methods (Player/Client)
def get_data(args, token):
stdout("Data received")
stdout("Now loading user ID: ```%s```" % str(args))
try:
y=json.loads(args)
passwd=y["passwd"]
if not passwd.isalnum():
raise Exception("Idiotic password")
if len(passwd) != 12:
raise Exception("Idiotic password")
vs=str(y["version"])
except:
return ERR_BAD
# Version check
if vs != CLIENTVERSION:
return ERR_OFF
# Maybe this token is already loaded to memory?
# FIXME: The token is not secretive enough, someone with
# the "token" can bypass all auths, so this is a risk.
try:
Player[token]["status"]+=1
Player[token]["status"]-=1
Player[token]["code"]=ERR_LOGIN
Player[token]["token"]=token
stdout("Player is already logged in")
# Delete user id, send payload, create user id again
uid=copy(Player[token]["userid"])
del Player[token]["userid"]
paydata=compress(Player[token])
Player[token]["userid"]=copy(uid)
del uid
# Remove other temporary data
del Player[token]["code"]
del Player[token]["token"]
return paydata
except:
pass
Player[token]=sql.load_player(token, passwd)
# Maybe something went wrong, or password is invalid
if (Player[token] == ERR_BAD):
del Player[token]
return ERR_BAD
if (Player[token] == ERR_ERR):
del Player[token]
return ERR_ERR
# Complete additional data
Player[token]["code"]=0
Player[token]["token"]=token
# Give you offline AP and refresh to NOW, round down
delta=(now()-Player[token]["aptime"])/AP_REGEN_TIME
Player[token]["ap"]=min(Player[token]["max_ap"], Player[token]["ap"]+delta)
Player[token]["aptime"]=now()
# This is only to start the timer if needed
if (Player[token]["ap"] < Player[token]["max_ap"]):
ApTimer[token]=Timer(AP_REGEN_TIME_F, ap_updater, args=[token])
ApTimer[token].daemon=True
ApTimer[token].start()
# Daily login rewards
Player[token]["code"]=daily_login(token)
# Remove UID from packet and save to JSON
uid=copy(Player[token]["userid"])
del Player[token]["userid"]
sjson=compress(Player[token])
Player[token]["userid"]=copy(uid)
# Clear temporary data, write internal big data fields
del uid
del Player[token]["code"]
del Player[token]["token"]
Player[token]["inv"]=sql.load_inv(token)
Player[token]["party_1"]=sql.load_party(token, 1)
Player[token]["party_2"]=sql.load_party(token, 2)
Player[token]["party_3"]=sql.load_party(token, 3)
# TODO: Load currency table
# TODO: Load event table
# Logged in
# {responseCode, token, status, gp, crystals, level, ap, max_ap, aptime }
return sjson
# TODO: This returns the inventory. Should take a "page" argument
def get_inv(args, token):
sjson=compress(Player[token]["inv"])
return sjson
# Sell units. Receives a single JSON array of inventory IDs.
def sellunits(args, token):
try:
y=json.loads(args)
gp=0
for tmp in y:
tmpa=int(tmp)
# Inventory size check (first check is not needed in a try loop...)
if tmpa >= len(Player[token]["inv"]):
raise Exception("Not in inventory")
if Player[token]["inv"][tmpa] == None:
stdout("None supplied (%d)" % tmpa)
raise Exception("Not in inventory")
# UNSELLABLE flag
un=dl_search(allunits, "unit_id",Player[token]["inv"][tmpa]["unit_id"])
if un["flags"] & UNSELLABLE:
stdout("This unit cannot be sold!")
raise Exception("This unit cannot be sold!")
# No duplicates
if y.count(tmp) != 1:
stdout("Duplication detected: %d" % (args.count(tmp)))
raise Exception("Duplicate index detected")
# Search in party
stdout("PARTY LOOKUP IN PROGRESS")
tmpb=dl_search(Player[token]["party_1"], "inv_id", tmpa)
if tmpb == "ERROR":
tmpb=dl_search(Player[token]["party_2"], "inv_id", tmpa)
if tmpb == "ERROR":
tmpb=dl_search(Player[token]["party_3"], "inv_id", tmpa)
if tmpb != "ERROR":
stdout("Unit is in party (%d)" % tmpb)
raise Exception("Party members can't be sold")
del tmpb
# No point wasting time, we have "un" so sum GP as well
am=calc_sell_price(un["rare"], Player[token]["inv"][tmpa]["level"])
if un["flags"] & DOUBLE_GP:
am*=2
gp+=am
del un, am
del tmpa
except:
return ERR_BAD
# Delete sold units and sum the GP
for idx in y:
Player[token]["inv"][idx]=None
Player[token]["gp"]+=int(gp)
sjson=compress('{"gp": %d, "profit": %d}' % (Player[token]["gp"], gp))
return sjson
# Upgrade units. Receives a JSON array: [UNIT-TO-UPGRADE, MAT1, MAT2, MAT3...]
# Based on invindex
def upgrade(args, token):
# Data validation (party members can't be used as material)
try:
y=json.loads(args)
w=False
for tmp in y:
tmpa=int(tmp)
# Inventory size check (first check is not needed in a try loop...)
if tmpa >= len(Player[token]["inv"]):
raise Exception("Not in inventory")
if Player[token]["inv"][tmpa] == None:
stdout("None supplied (%d)" % tmpa)
raise Exception("Not in inventory")
# First entry can be in the party, but must be uppable
if not w:
w=True
un=dl_search(allunits, "unit_id",Player[token]["inv"][tmpa]["unit_id"])
stdout("Unit flags:")
stdout("%s" % un["flags"])
#if un["flags"] & NO_LVLUP: # FIXME: Makes no sense now
# stdout("This unit cannot level up!")
# raise Exception("This unit cannot level up!")
target_ele=un["attribute"]
del un
continue
# Self fusion??
stdout("begin: starting")
if y.count(tmp) != 1:
stdout("Duplication detected: %d" % (args.count(tmp)))
raise Exception("Duplicate index or self fusion detected")
# Search in party
stdout("LOOKUP IN PROGRESS")
tmpb=dl_search(Player[token]["party_1"], "inv_id", tmpa)
stdout("dl_search OK")
if tmpb == "ERROR":
tmpb=dl_search(Player[token]["party_2"], "inv_id", tmpa)
if tmpb == "ERROR":
tmpb=dl_search(Player[token]["party_3"], "inv_id", tmpa)
if tmpb != "ERROR":
stdout("Unit is in party (%d)" % tmpb)
raise Exception("Party members can't be used as material")
del tmpb
del tmpa, w
except:
return ERR_BAD
# Get "target" inv id
target=y.pop(0)
stdout("Target is (%d)" % target)
# Define the experience you'll get by draining the relevant indexes
xp=0
for idx in y:
uxp=0
ud=Player[token]["inv"][idx]["unit_id"]
un=dl_search(allunits, "unit_id", ud)
try:
r=un["rare"]
f=un["flags"]
e=un["attribute"]
next_exp=exp_to_lvlup(Player[token]["inv"][idx]["level"])
except:
r=1
f=0
e=-1
stdout("ERROR, INVALID UNIT ID: %d" % ud)
# Units with EXP_UP are always "max-level"
if f & EXP_UP:
lv=10+(r*10)
else:
lv=Player[token]["inv"][idx]["level"]
uxp+=lv*20.0*((r+1)/2.0)
uxp+=Player[token]["inv"][idx]["exp"]/next_exp*100.0*20.0
# Flags and same element bonus
if f & EXP_UP:
uxp*=1.5
if e == target_ele:
uxp*=1.2
# Give the exp, and remove from inventory
xp+=int(uxp)
Player[token]["inv"][idx]=None
# We now have the experience, so we grant it
grant_exp(token, target, xp)
r=check_level_up(token, target)
sjson=compress(r)
return sjson
# args is the party ID
def get_party(args, token):
try:
pid=int(args)
if (pid > MAX_PARTIES):
raise Exception("too many parties")
except:
return ERR_BAD
sjson=compress(Player[token]["party_%d" % pid])
return sjson
# TODO: Obviously this is also a WIP
# {"party_id": pid, "formation": [p1, p2, p3, p4]}
def set_party(args, token):
# Standard checks
try:
y=json.loads(args)
tmp=y["party_id"]
pid=int(tmp)
# Maximum party number
if (pid > MAX_PARTIES):
raise Exception("too many parties")
# Only integers
for ele in y["formation"]:
tmp=int(ele)
# If the unit is not valid (in inventory), this will EXPLODE
un=dl_search(allunits, "unit_id", Player[token]["inv"][tmp]["unit_id"])
if un["flags"] & NO_PARTY:
raise Exception("This unit cannot be part of a party!")
del un
except:
return ERR_BAD
# FIXME: We can't have duplicates Oh My :o
#Player[token]["party_1"]=[]
stdout("Request to edit party %d" % pid)
# Check each request before appending
for i, idx in enumerate(y["formation"]):
stdout("Now checking (%d, %d)" % (i, idx))
# Empty the index in analysis
Player[token]["party_%s" % pid][i]={"unit_id": 0,
"inv_id": -1}
# Ignored index
if (idx < 0):
continue
# Duplicate checking!
if (Player[token]["inv"][idx]["unit_id"] in party_dupcheck(token, pid)):
return ERR_BAD
# Retrieve inventory data and replace it in player tokens
Player[token]["party_%s" % pid][i]["unit_id"]=Player[token]["inv"][idx]["unit_id"]
Player[token]["party_%s" % pid][i]["inv_id"]=idx
return ERR_DONE
# Evolve units. Receives a JSON array: [UNIT-TO-EVOLVE, MAT1, MAT2]
# Based on invindex
def evolve(args, token):
# Data validation (party members can't be used as material)
try:
y=json.loads(args)
if len(y) != 3:
return ERR_BAD
w=False
for tmp in y:
tmpa=int(tmp)
# Inventory size check (first check is not needed in a try loop...)
if tmpa >= len(Player[token]["inv"]):
raise Exception("Not in inventory")
if Player[token]["inv"][tmpa] == None:
stdout("None supplied (%d)" % tmpa)
raise Exception("Not in inventory")
# First entry can be in the party, but must be MAX LEVEL and level-able
if not w:
w=True
un=dl_search(allunits, "unit_id",Player[token]["inv"][tmpa]["unit_id"])
stdout("Unit flags:")
stdout("%s" % un["flags"])
#if un["flags"] & NO_LVLUP: # FIXME: Makes no sense now
# stdout("This unit cannot level up!")
# raise Exception("This unit cannot level up!")
if un["max_level"] != Player[token]["inv"][tmpa]["level"]:
stdout("raise: Level not yet maxed")
raise Exception("Not yet max level!")
target_ele=un["attribute"]
target_id=un["unit_id"]
target_rare=un["rare"]
# Check if evolved version exists
evolved=dl_search(allunits, "unit_id",Player[token]["inv"][tmpa]["unit_id"]+1)
evolved_id=evolved["unit_id"] # Same as raise Exception if can't evolve
del un
continue
# Self fusion??
stdout("begin: starting")
if y.count(tmp) != 1:
stdout("Duplication detected: %d" % (args.count(tmp)))
raise Exception("Duplicate index or self fusion detected")
# Search in party
stdout("LOOKUP IN PROGRESS")
tmpb=dl_search(Player[token]["party_1"], "inv_id", tmpa)
stdout("dl_search OK")
if tmpb == "ERROR":
tmpb=dl_search(Player[token]["party_2"], "inv_id", tmpa)
if tmpb == "ERROR":
tmpb=dl_search(Player[token]["party_3"], "inv_id", tmpa)
if tmpb != "ERROR":
stdout("Unit is in party (%d)" % tmpb)
raise Exception("Party members can't be used as material")
# TODO: Check if it is suitable material (same rarity etc.)
r=False
tmpb=dl_search(allunits, "unit_id",Player[token]["inv"][tmpa]["unit_id"])
if tmpb["rare"] == target_rare:
if tmpb["flags"] & SUPEREVOMAT:
r=True
elif tmpb["flags"] & EVO_MAT:
if tmpb["attribute"] == target_ele:
r=True
elif tmpb["unit_id"] == target_id:
r=True
if not r:
stdout("raise: Invalid evolution material")
raise Exception("Invalid evolve material")
del tmpb
del tmpa, w
except:
return ERR_BAD
# Get "target" inv id
#target=y.pop(0)
stdout("Target is (%d)" % target_id)
# Evolve and remove material. Clear level/exp as well.
Player[token]["inv"][y[0]]["unit_id"]+=1
Player[token]["inv"][y[0]]["level"]=0
Player[token]["inv"][y[0]]["exp"]=0
Player[token]["inv"][y[1]]=None
Player[token]["inv"][y[2]]=None
r=ERR_OK
# TODO: Update party, NOT high priority
try:
tmpb=dl_search(Player[token]["party_1"], "inv_id", target_id)
if tmpb != "ERROR":
tmpb["unit_id"]+=1
tmpb=dl_search(Player[token]["party_2"], "inv_id", target_id)
if tmpb != "ERROR":
tmpb["unit_id"]+=1
tmpb=dl_search(Player[token]["party_3"], "inv_id", target_id)
if tmpb != "ERROR":
tmpb["unit_id"]+=1
# Now, do this cause a duplicate? If yes, unsocket it!
tmpa=0
for tmpb in Player[token]["party_1"]:
if tmpb["unit_id"] == evolved_id:
tmpa+=1
if tmpa > 1:
tmpb["unit_id"]=-1
tmpb["inv_id"]=-1
stdout("Unsocket")
tmpa=0
for tmpb in Player[token]["party_2"]:
if tmpb["unit_id"] == evolved_id:
tmpa+=1
if tmpa > 1:
tmpb["unit_id"]=-1
tmpb["inv_id"]=-1
stdout("Unsocket")
tmpa=0
for tmpb in Player[token]["party_3"]:
if tmpb["unit_id"] == evolved_id:
tmpa+=1
if tmpa > 1:
tmpb["unit_id"]=-1
tmpb["inv_id"]=-1
stdout("Unsocket")
except:
pass
sjson=compress(r)
return sjson
# Creates a new account. Arguments: email
def register(args, token):
stdout("Request to register an account: %s" % str(args))
# https://emailregex.com/email-validation-summary/ - RFC allows more emails
# But we don't want to risk compromising the database, and some are dump
regex = '^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$'
try:
y=json.loads(args)
tmp=y["email"]
mail=str(tmp)
# Check if email is valid with above regex
if not re.search(regex,mail):
raise Exception("Not a valid email")
except:
return ERR_BAD
# TODO: "duplicate": all emails which difference consists on ponctuation
#email=mail.replace(".", "").replace("_", "").replace("-", "").replace("+", "")
stdout("Initial checks succeded")
# Check if email is already registered
check=sql.query_email(mail)
if (check != ""):
stdout("WARNING: Tried to register email \"%s\" (belongs to account %s)" % (mail, check))
return ERR_BAD
stdout("Now registering account")
# Register it
data=sql.add_player(mail)
if (data["userid"] <= 0):
return ERR_ERR
# From data, we have: userid, password
return compress(data)