db_store.create_backups_db()
db_store.create_contracts_db()
+contracts_motd = "Welcome to reEgg Server Emulator\nLeggacy contracts available every Monday/Friday"
+
def calculate_backup_checksum(SaveBackup):
# WIP - Need to figure out what the 0 and 61 still are.
# 61 - best fit is SaveBackup.mission.missions array length, which same time makes zero sense
GiftResponse.seconds_to_next_day = 86400 - DateInfo.seconds
return base64.b64encode(GiftResponse.SerializeToString())
+def populate_contracts_response(obj):
+ if obj is None:
+ obj = EIProto.ContractsResponse()
+ obj.warning_message = contracts_motd
+ for contract in contracts.get_active_contracts():
+ c = obj.contracts.add()
+ c.CopyFrom(contract)
+ return obj
+
+@app.route('/ei/get_contracts', methods=['POST'])
+def ei_get_contracts():
+ return base64.b64encode(populate_contracts_response(None).SerializeToString())
+
@app.route('/ei/get_periodicals', methods=['POST'])
def ei_periodicals_request():
PeriodicalResp = EIProto.PeriodicalsResponse()
- PeriodicalResp.contracts.warning_message = "Welcome to reEgg Server Emulator\nLeggacy contracts available every Monday/Friday"
+ populate_contracts_response(PeriodicalResp.contracts)
for evt in events.get_active_events():
e = PeriodicalResp.events.events.add()
e.CopyFrom(evt)
- for contract in contracts.get_active_contracts():
- c = PeriodicalResp.contracts.contracts.add()
- c.CopyFrom(contract)
return base64.b64encode(PeriodicalResp.SerializeToString())
@app.route('/ei/query_coop', methods=['POST'])
if QueryCoopResp.exists:
if QueryCoop.league != db_query:
QueryCoopResp.different_league = True
- # TODO: Ask contract defs for max coop allowed.
- #print(QueryCoopResp)
+ else:
+ ContractInfo = contracts.get_contract_by_identifier(QueryCoop.contract_identifier)
+ if db_store.is_coop_full(QueryCoop.coop_identifier, ContractInfo.max_coop_size):
+ QueryCoopResp.full = True
return base64.b64encode(QueryCoopResp.SerializeToString())
@app.route('/ei/create_coop', methods=['POST'])
Resp.finalized = True
return base64.b64encode(Resp.SerializeToString())
+@app.route('/ei/join_coop', methods=['POST'])
+def ei_join_coop():
+ data = base64.b64decode(request.form["data"].replace(" ", "+"))
+ JoinCoopRequest = EIProto.JoinCoopRequest()
+ JoinCoopRequest.ParseFromString(data)
+ JoinResponse = EIProto.JoinCoopResponse()
+ JoinResponse.coop_identifier = JoinCoopRequest.coop_identifier
+ db_query = db_store.is_coop_identifier_used(JoinCoopRequest.coop_identifier)
+ if db_query is None:
+ JoinResponse.success = False
+ JoinResponse.message = "That co-op doesn't exist."
+ return base64.b64encode(JoinResponse.SerializeToString())
+ if db_query != JoinCoopRequest.league:
+ JoinResponse.success = False
+ JoinResponse.message = "You can't join a " + ("Elite" if db_query == 1 else "Standard") + " contract."
+ return base64.b64encode(JoinResponse.SerializeToString())
+ BaseInfo = db_store.get_contract_info(JoinCoopRequest.coop_identifier)
+ ContribInfo = db_store.get_coop_contributors(JoinCoopRequest.coop_identifier)
+ ContractInfo = contracts.get_contract_by_identifier(BaseInfo[2])
+ if len(ContribInfo) - 1 >= ContractInfo.max_coop_size:
+ JoinResponse.success = False
+ JoinResponse.message = "Co-op is full!"
+ return base64.b64encode(JoinResponse.SerializeToString())
+ if BaseInfo[2] != JoinCoopRequest.contract_identifier:
+ JoinResponse.success = False
+ JoinResponse.message = "This co-op is not made for this contract."
+ return base64.b64encode(JoinResponse.SerializeToString())
+ # TODO: bans from coops
+ db_store.insert_coop_contribution(JoinCoopRequest.coop_identifier, JoinCoopRequest.user_id, JoinCoopRequest.user_name, JoinCoopRequest.soul_power)
+ JoinResponse.success = True
+ JoinResponse.banned = False
+ JoinResponse.seconds_remaining = (BaseInfo[4] + int(ContractInfo.length_seconds)) - int(time.time())
+ return base64.b64encode(JoinResponse.SerializeToString())
+
@app.route('/ei/auto_join_coop', methods=['POST'])
def ei_auto_join_coop():
data = base64.b64decode(request.form["data"].replace(" ", "+"))
AutoJoinCoopRequest = EIProto.AutoJoinCoopRequest()
AutoJoinCoopRequest.ParseFromString(data)
- print(AutoJoinCoopRequest)
Resp = EIProto.JoinCoopResponse()
+ Contract = contracts.get_contract_by_identifier(AutoJoinCoopRequest.contract_identifier)
+ if Contract is None:
+ Resp.success = False
+ Resp.message = "Invalid contract."
+ return base64.b64encode(Resp.SerializeToString())
+ coops = db_store.get_public_coops(AutoJoinCoopRequest.contract_identifier)
Resp.success = False
- Resp.message = "Unable to find any public co-ops to join."
+ for coop in coops:
+ coop_identifier = coop[0]
+ # TODO: Ban check
+ if not db_store.is_coop_full(coop_identifier, Contract.max_coop_size):
+ Resp.success = True
+ db_store.insert_coop_contribution(coop_identifier, AutoJoinCoopRequest.user_id, AutoJoinCoopRequest.user_name, AutoJoinCoopRequest.soul_power)
+ BaseInfo = db_store.get_contract_info(coop_identifier)
+ Resp.coop_identifier = coop_identifier
+ Resp.banned = False
+ Resp.seconds_remaining = (BaseInfo[4] + int(Contract.length_seconds)) - int(time.time())
+ break
+ if Resp.success == False:
+ Resp.message = "No public contracts found."
+ # TODO: Auto-create co-op if none found
return base64.b64encode(Resp.SerializeToString())
+@app.route('/ei/leave_coop', methods=['POST'])
+def ei_leave_coop():
+ data = base64.b64decode(request.form["data"].replace(" ", "+"))
+ LeaveCoopRequest = EIProto.LeaveCoopRequest()
+ LeaveCoopRequest.ParseFromString(data)
+ db_store.erase_coop_contribution(LeaveCoopRequest.coop_identifier, LeaveCoopRequest.player_identifier)
+ # TODO: Deepdive ghidra to see what this expects?
+ return ""
+
+@app.route('/ei/update_coop_permissions', methods=['POST'])
+def ei_update_coop_permissions():
+ data = base64.b64decode(request.form["data"].replace(" ", "+"))
+ PermUpdateReq = EIProto.UpdateCoopPermissionsRequest()
+ PermUpdateReq.ParseFromString(data)
+ PermUpdateResp = EIProto.UpdateCoopPermissionsResponse()
+ BaseInfo = db_store.get_contract_info(PermUpdateReq.coop_identifier)
+ if BaseInfo[5] != PermUpdateReq.requesting_user_id:
+ PermUpdateResp.success = False
+ PermUpdateResp.message = "Only the co-op creator can change the permissions."
+ return base64.b64encode(PermUpdateResp.SerializeToString())
+ db_store.change_coop_public_state(PermUpdateReq.coop_identifier, PermUpdateReq.public)
+ PermUpdateResp.success = True
+ return base64.b64encode(PermUpdateResp.SerializeToString())
+
@app.route('/ei/<path:subpath>', methods=['POST'])
def ei_unidentified_routes(subpath):
print("UNIMPLEMENTED REQ: /ei/" + subpath)
con = get_connection("contracts")
cur = con.cursor()
try:
- cur.execute("CREATE TABLE Contracts(ID INTEGER PRIMARY KEY AUTOINCREMENT, CoopName TEXT, ContractName TEXT, League SMALLINT, ContractStamp BIGINT, OwnerDevice TEXT)")
+ cur.execute("CREATE TABLE Contracts(ID INTEGER PRIMARY KEY AUTOINCREMENT, CoopName TEXT, ContractName TEXT, League SMALLINT, ContractStamp BIGINT, OwnerDevice TEXT, Public BOOL)")
cur.execute("CREATE TABLE ContractMember(DeviceID TEXT, CoopName TEXT, DisplayName TEXT, LastVisit BIGINT, Contribution BIGINT, ContribRate BIGINT, SoulPower DOUBLE, BoostTokens INTEGER, TimeCheats INT, Banned BOOL, LeftCoop BOOL)")
cur.execute("CREATE TABLE ContractGift(DeviceID TEXT, RewardType TEXT, Quantity INTEGER)")
except:
cur = con.cursor()
res = cur.execute('SELECT COUNT(DeviceID) FROM ContractMember WHERE CoopName="' + coop_identifier + '"')
retval = res.fetchone()
- print(retval)
con.close()
- return False
+ if retval is None:
+ return False
+ return retval[0] >= max_members
+
+def get_public_coops(contract_identifier):
+ con = get_connection("contracts")
+ cur = con.cursor()
+ res = cur.execute('SELECT CoopName FROM Contracts WHERE Public=1 AND ContractName="' + contract_identifier + '"')
+ ret = res.fetchall()
+ con.close()
+ if ret is None:
+ return []
+ else:
+ return ret
+
+def change_coop_public_state(coop_identifier, public):
+ if not is_coop_identifier_used(coop_identifier):
+ return False
+ con = get_connection("contracts")
+ cur = con.cursor()
+ cur.execute("UPDATE Contracts SET Public=? WHERE CoopName=?", (public, coop_identifier))
+ con.commit()
+ con.close()
+ return True
+
def is_coop_identifier_used(coop_identifier):
if not coop_identifier.isalnum():
res = cur.execute('SELECT League FROM Contracts WHERE CoopName="' + coop_identifier + '"')
retval = res.fetchone()
con.close()
- return retval
+ return retval[0] if retval is not None else None
def create_coop_contract(coop_identifier, contract_id, league, stamp, device_id, display_name):
if is_coop_identifier_used(coop_identifier):
con.close()
return True
+def insert_coop_contribution(coop_identifier, device_id, device_name, soul_power):
+ if not is_coop_identifier_used(coop_identifier):
+ return False
+ if not device_id.isalnum():
+ return False
+ con = get_connection("contracts")
+ cur = con.cursor()
+ stamp = int(time.time())
+ values = (device_id, coop_identifier, device_name, int(time.time()), 0, 0, soul_power)
+ cur.execute("INSERT INTO ContractMember(DeviceID, CoopName, DisplayName, LastVisit, Contribution, ContribRate, SoulPower) VALUES(?, ?, ?, ?, ?, ?, ?)", values)
+ con.commit()
+ con.close()
+ return True
+
+def erase_coop_contribution(coop_identifier, device_id):
+ if not is_coop_identifier_used(coop_identifier):
+ return
+ if not device_id.isalnum():
+ return
+ con = get_connection("contracts")
+ cur = con.cursor()
+ cur.execute("DELETE FROM ContractMember WHERE DeviceID=? AND CoopName=?", (device_id, coop_identifier))
+ con.commit()
+ con.close()
+ return
+
def get_coop_memberships(device_id):
if not device_id.isalnum():
return None