Add initial PoC
This commit is contained in:
commit
95b564a6e3
1 changed files with 228 additions and 0 deletions
228
schlafana.py
Normal file
228
schlafana.py
Normal file
|
@ -0,0 +1,228 @@
|
|||
#!/usr/bin/env python
|
||||
import pdb
|
||||
import json
|
||||
import logging
|
||||
from pprint import pprint
|
||||
from websocket import create_connection
|
||||
from prometheus_client import Counter, Summary, start_http_server
|
||||
from prometheus_client.core import CounterMetricFamily, REGISTRY
|
||||
import time
|
||||
|
||||
MESSAGES = { "GameInfo" : 0x00,
|
||||
"WorldUpdate" : 0x01,
|
||||
"BotSpawn" : 0x20,
|
||||
"BotKill" : 0x21,
|
||||
"BotMove" : 0x22,
|
||||
"BotStats" : 0x24
|
||||
}
|
||||
|
||||
|
||||
class BotDeathCollector(object):
|
||||
def collect(self):
|
||||
c = CounterMetricFamily("deaths", "Deaths of each player", labels=["name", "bot_rev"])
|
||||
for user in game.users.users:
|
||||
user = game.users.users.get(user)
|
||||
c.add_metric([user.name, user.current_rev], user.deaths)
|
||||
yield c
|
||||
|
||||
|
||||
class BotKillCollector(object):
|
||||
def collect(self):
|
||||
c = CounterMetricFamily("kills", "Kills of each player", labels=["name", "bot_rev"])
|
||||
for user in game.users.users:
|
||||
user = game.users.users.get(user)
|
||||
c.add_metric([user.name, user.current_rev], user.kills)
|
||||
yield c
|
||||
|
||||
|
||||
class BotRevisionCountCollector(object):
|
||||
def collect(self):
|
||||
c = CounterMetricFamily("code_revs", "Number of code revisions", labels=["name"])
|
||||
for user in game.users.users:
|
||||
user = game.users.users.get(user)
|
||||
c.add_metric([user.name], len(user.revs))
|
||||
yield c
|
||||
|
||||
|
||||
def main():
|
||||
global game
|
||||
game = Game()
|
||||
global logger
|
||||
logger = logging.getLogger("schlafana")
|
||||
logger.setLevel(logging.DEBUG)
|
||||
|
||||
ch = logging.StreamHandler()
|
||||
ch.setLevel(logging.DEBUG)
|
||||
|
||||
formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")
|
||||
ch.setFormatter(formatter)
|
||||
|
||||
logger.addHandler(ch)
|
||||
REGISTRY.register(BotDeathCollector())
|
||||
REGISTRY.register(BotKillCollector())
|
||||
REGISTRY.register(BotRevisionCountCollector())
|
||||
start_http_server(9000)
|
||||
logger.info("Start prometheus scrape endpoint")
|
||||
|
||||
while True:
|
||||
try:
|
||||
logger.warning("Trying to connect…")
|
||||
ws = create_connection("wss://schlangen.bytewerk.org/websocket")
|
||||
except Exception:
|
||||
game.uptime.observe(0)
|
||||
time.sleep(5)
|
||||
try:
|
||||
while True:
|
||||
data = ws.recv()
|
||||
game.uptime.observe(1)
|
||||
game.parse_data(data)
|
||||
except Exception:
|
||||
time.sleep(0.01)
|
||||
|
||||
|
||||
class Game():
|
||||
def __init__(self):
|
||||
self.x = 0
|
||||
self.y = 0
|
||||
self.food_decay_per_frame = 0
|
||||
self.users = UserList()
|
||||
self.uptime = Summary("api_up", "Status of the server API")
|
||||
pass
|
||||
|
||||
|
||||
def parse_data(self, data):
|
||||
js = json.loads(data)
|
||||
msg_type = js.get("t")
|
||||
|
||||
typ = MESSAGES.get(msg_type, -1)
|
||||
|
||||
if typ == MESSAGES["BotSpawn"]:
|
||||
bot = js.get("bot")
|
||||
self.users.update_by_db(db_id=bot.get("db_id"), data=bot)
|
||||
user = self.users.get_user(bot.get("db_id"))
|
||||
#print("{}({}): respawn".format(user.name, user.db_id))
|
||||
elif typ == MESSAGES["GameInfo"]:
|
||||
pdb.set_trace()
|
||||
game.x = 0
|
||||
game.y = 0
|
||||
elif typ == MESSAGES["BotKill"]:
|
||||
try:
|
||||
victim = self.users.get_user_by_rev(js.get("victim_id"))
|
||||
killer = self.users.get_user_by_rev(js.get("killer_id"))
|
||||
if js.get("killer_id") == js.get("victim_id"):
|
||||
#print("☕ {} killed himself".format(victim.name))
|
||||
pass
|
||||
else:
|
||||
#print("☠ {} fragged {}".format(killer.name, victim.name))
|
||||
pass
|
||||
killer.kills += 1
|
||||
victim.deaths += 1
|
||||
except AttributeError:
|
||||
print("Error in kill: {}".format(js))
|
||||
pass
|
||||
elif typ == MESSAGES["BotStats"]:
|
||||
msg_data = js.get("data")
|
||||
for rev in msg_data:
|
||||
user = self.users.get_user_by_rev(rev)
|
||||
print(user)
|
||||
pdb.set_trace()
|
||||
pass
|
||||
elif typ == MESSAGES["WorldUpdate"]:
|
||||
bots = js.get("bots")
|
||||
for bot in bots:
|
||||
bot = bots.get(bot)
|
||||
self.users.update_by_db(bot.get("db_id"), bot)
|
||||
|
||||
elif msg_type != "FoodSpawn" and msg_type != "WorldUpdate" and msg_type != "FoodConsume" and msg_type != "BotMoveHead" and msg_type != "Tick" and msg_type != "FoodDecay":
|
||||
logging.fatal("Unhandled msg_type: {}".format(msg_type))
|
||||
|
||||
|
||||
class BotUser():
|
||||
def __init__(self, data):
|
||||
self.iterations = []
|
||||
self.db_id = None
|
||||
self.revs = {}
|
||||
self.current_rev = None
|
||||
self.__deaths = 0
|
||||
self.__kills = 0
|
||||
self.parse(data)
|
||||
|
||||
@property
|
||||
def kills(self):
|
||||
return self.__kills
|
||||
|
||||
@kills.setter
|
||||
def kills(self, kills):
|
||||
self.__kills = kills
|
||||
self.revs.get(self.current_rev).kills += 1
|
||||
|
||||
@property
|
||||
def deaths(self):
|
||||
return self.__deaths
|
||||
|
||||
@deaths.setter
|
||||
def deaths(self, deaths):
|
||||
self.__deaths = deaths
|
||||
if self.__deaths % 2 == 0:
|
||||
print("{} was already killed {} times".format(self.name, self.__deaths))
|
||||
self.revs.get(self.current_rev).deaths += 1
|
||||
|
||||
def parse(self, data):
|
||||
self.db_id = data.get("db_id")
|
||||
self.name = data.get("name")
|
||||
self.dog_tag = data.get("dog_tag")
|
||||
self.face = data.get("face")
|
||||
self.revs.update({ data.get("id") : BotRevision(data) })
|
||||
self.current_rev = data.get("id")
|
||||
|
||||
def update(self, data):
|
||||
if self.current_rev in self.revs.keys():
|
||||
pass
|
||||
else:
|
||||
#print("new rev added")
|
||||
pass
|
||||
if self.current_rev != data.get("id"):
|
||||
self.current_rev = data.get("id")
|
||||
else:
|
||||
print("{} new code-version".format(self.name))
|
||||
self.revs.update({ self.current_rev : BotRevision(data) })
|
||||
|
||||
|
||||
class BotRevision():
|
||||
def __init__(self, data):
|
||||
self.id = data.get("id")
|
||||
self.deaths = 0
|
||||
self.kills = 0
|
||||
self.natural_food_consumed = 0
|
||||
self.carrion_food_consumed = 0
|
||||
self.hunted_food_consumed = 0
|
||||
pass
|
||||
|
||||
def parse(self, data):
|
||||
pass
|
||||
|
||||
|
||||
class UserList():
|
||||
def __init__(self):
|
||||
self.users = {}
|
||||
|
||||
def update_by_db(self, db_id, data):
|
||||
if data.get("db_id") in self.users.keys():
|
||||
self.users.get(data.get("db_id")).update(data)
|
||||
else:
|
||||
self.users.update({db_id : BotUser(data)})
|
||||
#print("{}({}): create user".format(self.users.get(db_id).name, db_id))
|
||||
|
||||
def get_user(self, db_id):
|
||||
return self.users.get(db_id)
|
||||
|
||||
def get_user_by_rev(self, rev_id):
|
||||
for user in self.users:
|
||||
user = self.users.get(user)
|
||||
for rev in user.revs:
|
||||
rev = user.revs.get(rev)
|
||||
if rev.id == rev_id:
|
||||
return user
|
||||
|
||||
|
||||
main()
|
Loading…
Reference in a new issue