Update server, support most methods
This commit is contained in:
parent
6d1807af11
commit
c8fa4d0dad
45
main.py
45
main.py
@ -43,6 +43,28 @@ async def handler(request: aiohttp.web.Request):
|
|||||||
print(f"{client=} accessed")
|
print(f"{client=} accessed")
|
||||||
return aiohttp.web.FileResponse('ui.html')
|
return aiohttp.web.FileResponse('ui.html')
|
||||||
|
|
||||||
|
@routes.post('/api/token{authtoken}/{method}')
|
||||||
|
async def handler(request: aiohttp.web.Request):
|
||||||
|
method = request.match_info.get('method', None)
|
||||||
|
token = request.match_info.get('authtoken', None)
|
||||||
|
model = request.app['model']
|
||||||
|
|
||||||
|
data = await request.json()
|
||||||
|
|
||||||
|
try:
|
||||||
|
assert method in model.ApiMethod.dict
|
||||||
|
value = await model.ApiMethod.dict[method](model, token, **data)
|
||||||
|
if value:
|
||||||
|
return aiohttp.web.json_response(value)
|
||||||
|
else:
|
||||||
|
return aiohttp.web.Response(status=200)
|
||||||
|
except Exception as e:
|
||||||
|
del e # unused?
|
||||||
|
traceback.print_exc()
|
||||||
|
return aiohttp.web.Response(status=400)
|
||||||
|
#finally:
|
||||||
|
# await model.send_state(client)
|
||||||
|
|
||||||
|
|
||||||
@routes.post('/api/{method}')
|
@routes.post('/api/{method}')
|
||||||
async def handler(request: aiohttp.web.Request):
|
async def handler(request: aiohttp.web.Request):
|
||||||
@ -52,7 +74,6 @@ async def handler(request: aiohttp.web.Request):
|
|||||||
|
|
||||||
data = await request.json()
|
data = await request.json()
|
||||||
|
|
||||||
print(f'{method=} {data=}')
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
assert method in model.ApiMethod.dict
|
assert method in model.ApiMethod.dict
|
||||||
@ -68,26 +89,6 @@ async def handler(request: aiohttp.web.Request):
|
|||||||
#finally:
|
#finally:
|
||||||
# await model.send_state(client)
|
# await model.send_state(client)
|
||||||
|
|
||||||
@routes.get(CLIENT_REGEX + '/ws')
|
|
||||||
async def _(request: aiohttp.web.Request):
|
|
||||||
client = get_client(request)
|
|
||||||
model = request.app['model']
|
|
||||||
|
|
||||||
ws = aiohttp.web.WebSocketResponse(heartbeat=10)
|
|
||||||
await ws.prepare(request)
|
|
||||||
|
|
||||||
print(f'[WS] client {client} connected, {ws=}')
|
|
||||||
await model.subscribe(client, ws)
|
|
||||||
|
|
||||||
async for msg in ws:
|
|
||||||
print(f'[WS] incoming message from client {client}, {ws=}, {msg=}')
|
|
||||||
|
|
||||||
print(f'[WS] client {client} disconnected, {ws=}')
|
|
||||||
model.unsubscribe(ws)
|
|
||||||
|
|
||||||
return ws
|
|
||||||
|
|
||||||
|
|
||||||
@routes.get(f"/api/{admintoken}")
|
@routes.get(f"/api/{admintoken}")
|
||||||
async def handler(request: aiohttp.web.Request):
|
async def handler(request: aiohttp.web.Request):
|
||||||
model = request.app['model']
|
model = request.app['model']
|
||||||
@ -116,7 +117,7 @@ if __name__ == '__main__':
|
|||||||
app.add_routes(routes)
|
app.add_routes(routes)
|
||||||
|
|
||||||
data = {}
|
data = {}
|
||||||
filename = "tehsession.json"
|
filename = "scoreboard.json"
|
||||||
if os.path.isfile(filename):
|
if os.path.isfile(filename):
|
||||||
with open(filename) as f:
|
with open(filename) as f:
|
||||||
data = json.load(f)
|
data = json.load(f)
|
||||||
|
427
model.py
427
model.py
@ -3,7 +3,7 @@ import base64
|
|||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import datetime
|
import datetime
|
||||||
|
import hashlib
|
||||||
|
|
||||||
def generate_random_id(_s=set()):
|
def generate_random_id(_s=set()):
|
||||||
while (new_id := base64.b32encode(bytearray(random.randint(0, 0xFF) for _ in range(10)))[:16].decode().lower()) in _s:
|
while (new_id := base64.b32encode(bytearray(random.randint(0, 0xFF) for _ in range(10)))[:16].decode().lower()) in _s:
|
||||||
@ -11,6 +11,10 @@ def generate_random_id(_s=set()):
|
|||||||
_s.add(new_id)
|
_s.add(new_id)
|
||||||
return new_id
|
return new_id
|
||||||
|
|
||||||
|
if not "WASCHMARKENSECRET" in os.environ:
|
||||||
|
print("Please set the environment variable WASCHMARKENSECRET first")
|
||||||
|
exit()
|
||||||
|
SECRET = os.environ["WASCHMARKENSECRET"]
|
||||||
|
|
||||||
class Model(object):
|
class Model(object):
|
||||||
class ApiMethod:
|
class ApiMethod:
|
||||||
@ -22,362 +26,117 @@ class Model(object):
|
|||||||
def __contains__(self, item):
|
def __contains__(self, item):
|
||||||
return item in self.dict
|
return item in self.dict
|
||||||
|
|
||||||
def __init__(self, model = {}):
|
def __init__(self, model = dict()):
|
||||||
if "sessions" not in model:
|
self.filename = "scoreboard.json"
|
||||||
model["sessions"] = []
|
if "users" not in model:
|
||||||
if "clients" not in model:
|
model["users"] = list()
|
||||||
model["clients"] = []
|
print(f"loaded model: {model}")
|
||||||
if "items" not in model:
|
self.users = { User(modelstring).uuid : User(modelstring) for modelstring in model["users"]}
|
||||||
model["items"] = []
|
|
||||||
self.sockets = {} # mapping: clientid -> sockets
|
#for _ in range(5):
|
||||||
self.sessions = { session: Session(model = model["sessions"][session]) for session in model["sessions"] }
|
# newuser = User()
|
||||||
self.clients = { client: Client(model = model["clients"][client]) for client in model["clients"] }
|
# self.users[newuser.uuid] = newuser
|
||||||
self.items = { item: Item(model = model["items"][item]) for item in model["items"] }
|
|
||||||
self.filename = "tehsession.json"
|
self.secretlookup = { self.users[uuid].get_secret() : uuid for uuid in self.users }
|
||||||
|
print(self.secretlookup)
|
||||||
|
#print(self.users)
|
||||||
|
|
||||||
def to_json(self):
|
def to_json(self):
|
||||||
model = {
|
model = {
|
||||||
"users": {session: self.sessions[session].to_json() for session in self.sessions },
|
"users": [self.users[user].to_json() for user in self.users ],
|
||||||
"clients": {client: self.clients[client].to_json() for client in self.clients },
|
|
||||||
"items": {item: self.items[item].to_json() for item in self.items },
|
|
||||||
}
|
}
|
||||||
return model
|
return model
|
||||||
|
|
||||||
@ApiMethod
|
def verify_user(self, authtoken):
|
||||||
async def get_public_model(self):
|
if authtoken in self.secretlookup:
|
||||||
publicmodel = {
|
return self.users[self.secretlookup[authtoken]]
|
||||||
"users" : [
|
raise Exception(f"Unauthorized user: {authtoken}")
|
||||||
{
|
|
||||||
"score": 50,
|
|
||||||
"maxscore": 75,
|
|
||||||
"timeout": 0,
|
|
||||||
"uuid": "wasisseneuuid?",
|
|
||||||
"name": "Knödelkind"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"score": 58,
|
|
||||||
"maxscore": 75,
|
|
||||||
"timeout": 1595019787,
|
|
||||||
"uuid": "neuuidsolleindeutigsein",
|
|
||||||
"name": "Dominickque"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"score": 69,
|
|
||||||
"maxscore": 100,
|
|
||||||
"timeout": 1595024761,
|
|
||||||
"uuid": "moneyboyswag420",
|
|
||||||
"name": "Andi"
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
return publicmodel
|
|
||||||
|
|
||||||
@ApiMethod
|
def verify_admin(self, authtoken):
|
||||||
async def test_api(self, clientid):
|
if authtoken == hashlib.sha256(SECRET.encode() + b"admintoken").hexdigest():
|
||||||
print(f'test_api {clientid=}')
|
return
|
||||||
|
raise Exception(f"Unauthorized admin: {authtoken}")
|
||||||
@ApiMethod
|
|
||||||
async def test_yeet(self, clientid):
|
|
||||||
raise Exception('yeet')
|
|
||||||
|
|
||||||
@ApiMethod
|
|
||||||
async def change_username(self, clientid, username) -> str:
|
|
||||||
self.clients[clientid].name = username
|
|
||||||
|
|
||||||
@ApiMethod
|
|
||||||
async def create_session(self, clientid, sessionname) -> str:
|
|
||||||
if not sessionname:
|
|
||||||
raise Exception(f"Sessionname cant be empty!")
|
|
||||||
session = Session(name = sessionname, owner = clientid)
|
|
||||||
self.sessions[session.id] = session
|
|
||||||
print("create_session was called")
|
|
||||||
|
|
||||||
@ApiMethod
|
|
||||||
async def change_sessionname(self, clientid, sessionid, sessionname) -> str:
|
|
||||||
if not sessionname:
|
|
||||||
raise Exception("Cant be empty!")
|
|
||||||
session = self.sessions[sessionid]
|
|
||||||
if not (session.owner == clientid):
|
|
||||||
raise Exception("Ownly owner can change sessionname")
|
|
||||||
session.name = sessionname
|
|
||||||
|
|
||||||
@ApiMethod
|
|
||||||
async def create_item(self, clientid, name, description, image):
|
|
||||||
client = self.clients[clientid]
|
|
||||||
if not client.session:
|
|
||||||
raise Exception("create_item requires the client to be host. But client is in no session")
|
|
||||||
session = self.sessions[client.session]
|
|
||||||
if (session.owner != client.id):
|
|
||||||
raise Exception("create_item requires the client to be host. But client is not the owner")
|
|
||||||
if not name:
|
|
||||||
raise Exception("create_item requires an item name")
|
|
||||||
item = Item(name = name, description = description, image = image)
|
|
||||||
self.items[item.id] = item
|
|
||||||
|
|
||||||
|
|
||||||
@ApiMethod
|
|
||||||
async def leave_session(self, clientid):
|
|
||||||
client = self.clients[clientid]
|
|
||||||
sessionid = client.session
|
|
||||||
if sessionid:
|
|
||||||
session = self.sessions[sessionid]
|
|
||||||
session.clients.remove(clientid)
|
|
||||||
self.clients[clientid].session = ""
|
|
||||||
|
|
||||||
@ApiMethod
|
|
||||||
async def join_session(self, clientid, sessionid):
|
|
||||||
client = self.clients[clientid]
|
|
||||||
old_sessionid = client.session
|
|
||||||
# leave old session
|
|
||||||
if old_sessionid:
|
|
||||||
old_session = self.sessions[old_sessionid]
|
|
||||||
old_session.clients.remove(clientid)
|
|
||||||
session = self.sessions[sessionid]
|
|
||||||
client.session = sessionid
|
|
||||||
session.clients.append(client.id)
|
|
||||||
|
|
||||||
|
|
||||||
@ApiMethod
|
|
||||||
async def move_item(self, clientid, fromplayer, toplayer, itemid, toslot, fromslot):
|
|
||||||
client = self.clients[clientid]
|
|
||||||
fromclient = next((client for client in self.clients.values() if client.name == fromplayer), None)
|
|
||||||
toclient = next((client for client in self.clients.values() if client.name == toplayer), None)
|
|
||||||
if fromplayer == "master":
|
|
||||||
# create item
|
|
||||||
if not toclient:
|
|
||||||
raise Exception("to-client is illegal")
|
|
||||||
session = self.sessions[client.session]
|
|
||||||
if not session:
|
|
||||||
raise Exception("move item must be used in session")
|
|
||||||
if client.id != session.owner:
|
|
||||||
raise Exception("Only owner can move items")
|
|
||||||
toslot -= 1
|
|
||||||
if toslot > len(session.inventories[toclient.id]):
|
|
||||||
raise Exception("Index out of toplayers range")
|
|
||||||
session.inventories[toclient.id].insert(toslot, itemid)
|
|
||||||
elif toplayer == "master":
|
|
||||||
if not fromclient:
|
|
||||||
raise Exception("from-client is illegal")
|
|
||||||
session = self.sessions[client.session]
|
|
||||||
if not session:
|
|
||||||
raise Exception("move item must be used in session")
|
|
||||||
if client.id != session.owner:
|
|
||||||
raise Exception("Only owner can move items")
|
|
||||||
if itemid not in session.inventories[fromclient.id]:
|
|
||||||
raise Exception("he does not have that item")
|
|
||||||
fromslot -= 1
|
|
||||||
if fromslot > len(session.inventories[fromclient.id]):
|
|
||||||
raise Exception("Index out of fromplayer range")
|
|
||||||
session.inventories[fromclient.id].pop(fromslot)
|
|
||||||
else:
|
|
||||||
if not fromclient and not toclient:
|
|
||||||
raise Exception("from- or to-client are illegal")
|
|
||||||
session = self.sessions[client.session]
|
|
||||||
if not session:
|
|
||||||
raise Exception("move item must be used in session")
|
|
||||||
if client.id != session.owner:
|
|
||||||
raise Exception("Only owner can move items")
|
|
||||||
if itemid not in session.inventories[fromclient.id]:
|
|
||||||
raise Exception("he does not have that item")
|
|
||||||
toslot -= 1
|
|
||||||
if toslot > len(session.inventories[toclient.id]):
|
|
||||||
raise Exception("Index out of toplayers range")
|
|
||||||
fromslot -= 1
|
|
||||||
if fromslot > len(session.inventories[fromclient.id]):
|
|
||||||
raise Exception("Index out of fromplayer range")
|
|
||||||
session.inventories[fromclient.id].pop(fromslot)
|
|
||||||
session.inventories[toclient.id].insert(toslot, itemid)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ApiMethod
|
|
||||||
async def draw(self, clientid):
|
|
||||||
await self.send_state(clientid)
|
|
||||||
|
|
||||||
async def send_lobby_view(self, clientid):
|
|
||||||
data = {}
|
|
||||||
client = self.clients[clientid]
|
|
||||||
|
|
||||||
data["view"] = "lobby"
|
|
||||||
data["username"] = client.name
|
|
||||||
data["sessions"] = {
|
|
||||||
session.id: {
|
|
||||||
"id": session.id,
|
|
||||||
"name": session.name,
|
|
||||||
"owned": session.owner == client.id,
|
|
||||||
} for session in self.sessions.values()
|
|
||||||
}
|
|
||||||
for socket in self.sockets[clientid]:
|
|
||||||
await socket.send_json(data)
|
|
||||||
|
|
||||||
async def send_master_view(self, clientid):
|
|
||||||
data = {}
|
|
||||||
client = self.clients[clientid]
|
|
||||||
session = self.sessions[client.session]
|
|
||||||
|
|
||||||
data["view"] = "master"
|
|
||||||
data["username"] = client.name
|
|
||||||
data["session"] = session.to_json()
|
|
||||||
data["items"] = self.to_json()["items"]
|
|
||||||
data["inventories"] = {}
|
|
||||||
for _client in session.inventories:
|
|
||||||
_client = self.clients[_client]
|
|
||||||
inventory = session.get_items(_client.id)
|
|
||||||
inventory = list(map(lambda itemid: self.to_json()["items"][itemid], inventory))
|
|
||||||
data["inventories"][_client.name] = inventory
|
|
||||||
|
|
||||||
for socket in self.sockets[clientid]:
|
|
||||||
await socket.send_json(data)
|
|
||||||
|
|
||||||
|
|
||||||
async def send_session_view(self, clientid):
|
|
||||||
data = {}
|
|
||||||
client = self.clients[clientid]
|
|
||||||
session = self.sessions[client.session]
|
|
||||||
|
|
||||||
data["view"] = "session"
|
|
||||||
data["username"] = client.name
|
|
||||||
data["session"] = session.name
|
|
||||||
data["inventories"] = {}
|
|
||||||
for _client in session.inventories:
|
|
||||||
_client = self.clients[_client]
|
|
||||||
inventory = session.get_items(_client.id)
|
|
||||||
inventory = list(map(lambda itemid: self.to_json()["items"][itemid], inventory))
|
|
||||||
data["inventories"][_client.name] = inventory
|
|
||||||
|
|
||||||
for socket in self.sockets[clientid]:
|
|
||||||
await socket.send_json(data)
|
|
||||||
|
|
||||||
async def send_state(self, clientid):
|
|
||||||
# TODO: compute state, send to client
|
|
||||||
data = {}
|
|
||||||
client = self.clients[clientid]
|
|
||||||
session = self.sessions[client.session] if client.session else None
|
|
||||||
if session:
|
|
||||||
if session.owner == client.id:
|
|
||||||
await self.send_master_view(clientid)
|
|
||||||
else:
|
|
||||||
await self.send_session_view(clientid)
|
|
||||||
else:
|
|
||||||
await self.send_lobby_view(clientid)
|
|
||||||
|
|
||||||
def save(self):
|
def save(self):
|
||||||
with open(self.filename, "w") as f:
|
with open(self.filename, "w") as f:
|
||||||
json.dump(self.to_json(), f)
|
json.dump(self.to_json(), f, indent=2)
|
||||||
|
|
||||||
def exists_client(self, clientid: str) -> bool:
|
#
|
||||||
return clientid in self.clients
|
# Public API Methods
|
||||||
|
#
|
||||||
|
|
||||||
def create_client(self, name="Joe") -> str:
|
@ApiMethod
|
||||||
if not name:
|
async def get_public_model(self):
|
||||||
raise Exception("Username cannot be empty!")
|
return self.to_json()
|
||||||
client = Client()
|
|
||||||
client.name = name
|
|
||||||
self.clients[client.id] = client
|
|
||||||
return client.id
|
|
||||||
|
|
||||||
async def subscribe(self, clientid, socket):
|
#
|
||||||
if not clientid in self.sockets:
|
# Authorized API Methods
|
||||||
self.sockets[clientid] = []
|
#
|
||||||
self.sockets[clientid].append(socket)
|
|
||||||
await self.send_state(clientid)
|
|
||||||
|
|
||||||
def unsubscribe(self, socket):
|
@ApiMethod
|
||||||
for client in self.sockets:
|
async def get_user(self, authtoken):
|
||||||
if socket in self.sockets[client]:
|
user = self.verify_user(authtoken)
|
||||||
self.sockets[client].remove(socket)
|
return {"user":user.name}
|
||||||
|
|
||||||
class Session:
|
@ApiMethod
|
||||||
|
async def set_score(self, authtoken, newscore):
|
||||||
|
user = self.verify_user(authtoken)
|
||||||
|
if newscore <= user.maxscore:
|
||||||
|
user.score = newscore
|
||||||
|
else:
|
||||||
|
raise Exception("Tried to raise user score above maxscore")
|
||||||
|
|
||||||
def __init__(self, model = None, owner = None, name = None):
|
#
|
||||||
|
# Admin API Methods
|
||||||
|
#
|
||||||
|
|
||||||
|
@ApiMethod
|
||||||
|
async def add_user(self, authtoken, username):
|
||||||
|
self.verify_admin(authtoken)
|
||||||
|
newuser = User(username = username)
|
||||||
|
self.users[newuser.uuid] = newuser
|
||||||
|
|
||||||
|
@ApiMethod
|
||||||
|
async def rename_user(self, authtoken, uuid, newusername):
|
||||||
|
self.verify_admin(authtoken)
|
||||||
|
if uuid in self.users:
|
||||||
|
self.users[uuid].name = newusername
|
||||||
|
else:
|
||||||
|
raise Exception("No such user")
|
||||||
|
|
||||||
|
@ApiMethod
|
||||||
|
async def set_maxscore(self, authtoken, newmaxscore):
|
||||||
|
self.verify_admin(authtoken)
|
||||||
|
if newscore <= user.maxscore:
|
||||||
|
user.score = newscore
|
||||||
|
else:
|
||||||
|
raise Exception("Tried to raise user score above maxscore")
|
||||||
|
|
||||||
|
class User:
|
||||||
|
|
||||||
|
def __init__(self, username = "Default Username", model = None):
|
||||||
if model:
|
if model:
|
||||||
self.id = model["id"]
|
self.uuid = model["uuid"]
|
||||||
self.name = model["name"]
|
self.name = model["name"]
|
||||||
self.clients = model["clients"]
|
self.score = model["score"]
|
||||||
self.owner = model["owner"]
|
self.maxscore = model["maxscore"]
|
||||||
if "inventories" in model:
|
self.timeout = model["timeout"]
|
||||||
self.inventories = model["inventories"]
|
|
||||||
else:
|
|
||||||
self.inventories = {}
|
|
||||||
elif owner and name:
|
|
||||||
self.id = generate_random_id()
|
|
||||||
self.clients = []
|
|
||||||
self.owner = owner
|
|
||||||
self.name = name
|
|
||||||
self.inventories = {}
|
|
||||||
else:
|
else:
|
||||||
raise Exception("Illegal session constructor")
|
self.uuid = generate_random_id()
|
||||||
|
self.name = username
|
||||||
|
self.score = 0
|
||||||
|
self.maxscore = 0
|
||||||
|
self.timeout = 0
|
||||||
|
|
||||||
def get_items(self, playerid):
|
def get_secret(self):
|
||||||
if playerid in self.inventories:
|
return hashlib.sha256(SECRET.encode() + str(self.uuid).encode()).hexdigest()
|
||||||
return self.inventories[playerid]
|
|
||||||
else:
|
|
||||||
return []
|
|
||||||
|
|
||||||
def give_item(self, playerid, itemid):
|
|
||||||
if not playerid in self.inventories:
|
|
||||||
self.inventories[playerid] = []
|
|
||||||
self.inventories[playerid].append(itemid)
|
|
||||||
|
|
||||||
def to_json(self):
|
def to_json(self):
|
||||||
model = {
|
model = {
|
||||||
"id": self.id,
|
"uuid": self.uuid,
|
||||||
"name": self.name,
|
"name": self.name,
|
||||||
"clients": self.clients,
|
"score": self.score,
|
||||||
"owner": self.owner,
|
"maxscore": self.maxscore,
|
||||||
"inventories": self.inventories
|
"timeout": self.timeout,
|
||||||
}
|
|
||||||
return model
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Client:
|
|
||||||
|
|
||||||
def __init__(self, model = None):
|
|
||||||
if model:
|
|
||||||
self.id = model["id"]
|
|
||||||
self.name = model["name"]
|
|
||||||
self.session = model["session"]
|
|
||||||
else:
|
|
||||||
self.id = generate_random_id()
|
|
||||||
self.name = "Default Client Name"
|
|
||||||
self.session = ""
|
|
||||||
|
|
||||||
def to_json(self):
|
|
||||||
model = {
|
|
||||||
"id": self.id,
|
|
||||||
"name": self.name,
|
|
||||||
"session": self.session,
|
|
||||||
}
|
|
||||||
return model
|
|
||||||
|
|
||||||
class Item:
|
|
||||||
|
|
||||||
def __init__(self, model = None, name = None, description = "", image = "", tags = {}):
|
|
||||||
if model:
|
|
||||||
self.id = model["id"]
|
|
||||||
self.name = model["name"]
|
|
||||||
self.description = model["description"]
|
|
||||||
self.image = model["image"]
|
|
||||||
if "tags" in model:
|
|
||||||
self.tags = model["tags"]
|
|
||||||
else:
|
|
||||||
self.tags = {}
|
|
||||||
elif name:
|
|
||||||
self.id = generate_random_id()
|
|
||||||
self.name = name
|
|
||||||
self.description = description
|
|
||||||
self.image = image
|
|
||||||
self.tags = tags
|
|
||||||
else:
|
|
||||||
raise Exception("Illegal Item Constructor")
|
|
||||||
|
|
||||||
def to_json(self):
|
|
||||||
model = {
|
|
||||||
"id": self.id,
|
|
||||||
"name": self.name,
|
|
||||||
"description": self.description,
|
|
||||||
"image": self.image,
|
|
||||||
"tags": self.tags,
|
|
||||||
}
|
}
|
||||||
return model
|
return model
|
||||||
|
99
scoreboard.json
Normal file
99
scoreboard.json
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
{
|
||||||
|
"users": [
|
||||||
|
{
|
||||||
|
"uuid": "bnqkcmu3vd3fq5fu",
|
||||||
|
"name": {
|
||||||
|
"uuid": "t55sfdftsozzy3mi",
|
||||||
|
"name": {
|
||||||
|
"uuid": "sprnnogu3cvzzsqf",
|
||||||
|
"name": "Default User Name",
|
||||||
|
"score": 0,
|
||||||
|
"maxscore": 0,
|
||||||
|
"timeout": 0
|
||||||
|
},
|
||||||
|
"score": 0,
|
||||||
|
"maxscore": 0,
|
||||||
|
"timeout": 0
|
||||||
|
},
|
||||||
|
"score": 0,
|
||||||
|
"maxscore": 0,
|
||||||
|
"timeout": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uuid": "sywskga5s3m6wxuq",
|
||||||
|
"name": {
|
||||||
|
"uuid": "zq2tratnuyfbfrx2",
|
||||||
|
"name": {
|
||||||
|
"uuid": "c5lrovzryckbxgwg",
|
||||||
|
"name": "Default User Name",
|
||||||
|
"score": 0,
|
||||||
|
"maxscore": 0,
|
||||||
|
"timeout": 0
|
||||||
|
},
|
||||||
|
"score": 0,
|
||||||
|
"maxscore": 0,
|
||||||
|
"timeout": 0
|
||||||
|
},
|
||||||
|
"score": 0,
|
||||||
|
"maxscore": 0,
|
||||||
|
"timeout": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uuid": "ytpvp4dopx54gfld",
|
||||||
|
"name": {
|
||||||
|
"uuid": "snmtkzofojtqma3a",
|
||||||
|
"name": {
|
||||||
|
"uuid": "wxl42rw4zdghjgve",
|
||||||
|
"name": "Default User Name",
|
||||||
|
"score": 0,
|
||||||
|
"maxscore": 0,
|
||||||
|
"timeout": 0
|
||||||
|
},
|
||||||
|
"score": 0,
|
||||||
|
"maxscore": 0,
|
||||||
|
"timeout": 0
|
||||||
|
},
|
||||||
|
"score": 0,
|
||||||
|
"maxscore": 0,
|
||||||
|
"timeout": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uuid": "dxkahhq65uo63xqp",
|
||||||
|
"name": {
|
||||||
|
"uuid": "6u5z6po6gtmh52fr",
|
||||||
|
"name": {
|
||||||
|
"uuid": "shnisaos4vxnfcvq",
|
||||||
|
"name": "Default User Name",
|
||||||
|
"score": 0,
|
||||||
|
"maxscore": 0,
|
||||||
|
"timeout": 0
|
||||||
|
},
|
||||||
|
"score": 0,
|
||||||
|
"maxscore": 0,
|
||||||
|
"timeout": 0
|
||||||
|
},
|
||||||
|
"score": 0,
|
||||||
|
"maxscore": 0,
|
||||||
|
"timeout": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uuid": "3uw4wif2heales72",
|
||||||
|
"name": {
|
||||||
|
"uuid": "fg7kxyv36ls6a6w5",
|
||||||
|
"name": {
|
||||||
|
"uuid": "72onbiw4phvtd54y",
|
||||||
|
"name": "Default User Name",
|
||||||
|
"score": 0,
|
||||||
|
"maxscore": 0,
|
||||||
|
"timeout": 0
|
||||||
|
},
|
||||||
|
"score": 0,
|
||||||
|
"maxscore": 0,
|
||||||
|
"timeout": 0
|
||||||
|
},
|
||||||
|
"score": 0,
|
||||||
|
"maxscore": 0,
|
||||||
|
"timeout": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -1 +0,0 @@
|
|||||||
{"users": {}, "clients": {}, "items": {}}
|
|
1
waschmarkensecret
Normal file
1
waschmarkensecret
Normal file
@ -0,0 +1 @@
|
|||||||
|
swiggityswootyiamcomingforthatwashingcoin
|
Loading…
Reference in New Issue
Block a user