giantsd/server.py

299 lines
11 KiB
Python

from dpnet.packet import Packet
from giants.map import Map
from giants.player import Player
from dpnet.netserver import Netserver
from dpnet.session import Session
import socket
import importlib
import os
from giants import Teams, GameTypes, ChatColor, ChatType
from utils.logger import setup_logger
import traceback
import asyncio
from aioconsole import ainput
import struct
from curses import wrapper
logger = setup_logger(__name__)
class Server:
def __init__(self, **kwargs):
self.listen_ip = kwargs.get("ip", "0.0.0.0")
self.listen_port = kwargs.get("port", 19711)
self.register_with_ms = kwargs.get("register", False)
self.teams = kwargs.get("teams", Teams.MvM)
self.game_type = kwargs.get("gametype", GameTypes.TeamDeathmatchWithFullBase)
self.currentmap = kwargs.get("map", Map("Testmap.gck"))
self.maxplayers = kwargs.get("maxplayers", 20)
self.name = kwargs.get("name", "Default Server Name")
fake_session = Session(self, socket.socket(socket.AF_INET, socket.SOCK_DGRAM))
fake_session.ip = "127.0.0.1"
fake_session.port = 3333
self.players = [Player("[Server]", fake_session)]
fake_player = Session(self, socket.socket(socket.AF_INET, socket.SOCK_DGRAM))
fake_player.ip = "127.0.0.1"
fake_player.port = 3334
self.players.append(Player("Bot 1", fake_player))
fake_player = Session(self, socket.socket(socket.AF_INET, socket.SOCK_DGRAM))
fake_player.ip = "127.0.0.1"
fake_player.port = 3334
self.players.append(Player("Bot 2", fake_player))
fake_player = Session(self, socket.socket(socket.AF_INET, socket.SOCK_DGRAM))
fake_player.ip = "127.0.0.1"
fake_player.port = 3334
self.players.append(Player("Bot 3", fake_player))
self.accept_new_players = True
self.version = 1.497
self.points_per_kill = 1
self.points_per_capture = 5
self.detente_time = 0 # minutes
self._plugins = []
self.tempplayers = []
self.running = True
self.ticks = 60
self._nextid = 118
def update(self):
#logger.debug("Calling update")
pass
async def add_player(self, player):
self.tempplayers.remove(player)
self.players.append(player)
await self.broadcast_event("on_player_join", player)
# todo: remove
def create_temp_player(self, player):
self.tempplayers.append(player)
async def remove_player(self, player):
self.players.remove(player)
def add_plugin(self, plugin):
self._plugins.append(plugin)
async def change_map(self, mappath):
try:
self.currentmap = Map(mappath)
await self.broadcast_event("on_map_change", self.currentmap)
except Exception:
logger.error("Could not change map")
async def broadcast_event(self, event, *args):
logger.debug("Broadcasting event "+event)
for plugin in self._plugins:
if hasattr(plugin, event):
func = getattr(plugin, event)
if callable(func):
try:
await func(*args)
except Exception:
logger.error("Could not call plugin function: "+plugin.__class__.__name__+"."+event)
traceback.print_exc()
def load_plugins(self):
plugins = os.listdir("plugins")
for plugin in plugins:
if os.path.isdir("plugins/"+plugin) or plugin == "__init__.py":
continue
if not plugin.endswith(".py"):
continue
pluginname = plugin.split(".py")[0]
try:
module = importlib.import_module("plugins."+pluginname)
if hasattr(module, "setup"):
setup = getattr(module, "setup")
if callable(setup):
logger.info("Loading plugin "+module.__name__)
module.setup(self)
except Exception:
logger.warning("Could not load plugin "+plugin)
traceback.print_exc()
async def broadcast_message(self, text, color=ChatColor.Orange, type=ChatType.All):
for player in self.players:
if player.name == "[Server]":
continue
await player.send_message(text, color=color, type=type)
async def ask_command(self):
while True:
try:
cmd = await ainput(">")
logger.debug("Sending game payload %s", cmd)
for player in self.players:
if player.name == "[Server]":
continue
await player.session.send_gamedata(bytes.fromhex(cmd), acknow=False)
except:
traceback.print_exc()
async def broadcast_gamedata(self, payload, **kwargs):
for player in self.players:
if player.name == "[Server]":
continue
await player.session.send_gamedata(payload, **kwargs)
async def broadcast_gamedata_except(self, player, payload, **kwargs):
for pplayer in self.players:
if pplayer.name == "[Server]" or pplayer == player:
continue
await pplayer.session.send_gamedata(payload, **kwargs)
async def broadcast_message_except(self, player, text, color=ChatColor.Yellow, type=ChatType.All):
for pplayer in self.players:
if pplayer.name == "[Server]" or pplayer == player:
continue
await pplayer.send_message(text, color=color, type=type)
async def new_object(self, model):
oid = self._nextid
p = Packet()
p.putByte(0x05) # opcode
p.putByte(0)
p.putShort(self._nextid)
p.putByte(0x00)
p.putULong(model)
p.putByte(0x00)
self._nextid += 1
await self.broadcast_gamedata(p.getvalue())
return oid
async def spawn(self, player, model):
plix = self.get_player_index(player)
p = Packet()
p.putByte(0x05) # opcode
p.putByte(plix)
p.putShort(self._nextid)
p.putByte(0x00)
p.putULong(model)
p.putByte(0x00)
await self.broadcast_gamedata(p.getvalue(), acknow=False)
#await self.broadcast_gamedata(bytes.fromhex("05" + plix + struct.pack("<H", self._nextid).hex() + "00" + model + "00000000"), acknow=False)
p = Packet()
p.putByte(0x0a) # opcode
p.putByte(0x12) # length
p.putByte(0x00) # unknown
p.putByte(0x00) # unknown
p.putByte(0x00) # unknown
p.putByte(plix) # player index
p.putShort(self._nextid) # object id
p.putByte(0x00) # unknown
p.putByte(0x01) # unknown
p.putULong(model) # model
p.putFloat(player.x) # player x
p.putFloat(player.y) # player y
p.putFloat(player.z) # player z
p.putULong(0x00) # unknown
p.putULong(0x00) # unknown
p.putULong(0x00) # unknown
p.putULong(0x00) # unknown
p.putULong(0x00) # orientation
p.putULong(0x00) # unknown
await self.broadcast_gamedata(p.getvalue(), acknow=False)
self._nextid+=1
#await self.broadcast_gamedata(bytes.fromhex("0a12000000" + plix + struct.pack("<H", self._nextid).hex() + "0003" + model + "00000000"), acknow=False)
#await self.broadcast_gamedata(bytes.fromhex("0a32000000" + plix + struct.pack("<B", self._nextid).hex() + "000001" + model + "000000ff00000000000000000000000000000000000000000000000000000000000000000000000a12000000" + plix + struct.pack("<B", self._nextid).hex() + "0000" + model + "020000000000000000"), acknow=False)
async def change_model(self, player, model):
plid = self.get_player_index(player)
# p = Packet()
# p.putByte(0x0a) # opcode
# p.putByte(0x12) # length
# p.putByte(0x00) # unknown
# p.putByte(0x00) # unknown
# p.putByte(0x00) # unknown
# p.putByte(plid) # player index
# p.putShort(player.oid) # object id
# p.putByte(0x00) # unknown
# p.putByte(0x02) # 4 -> death, 3 -> little move
# p.putULong(model) # model
# p.putFloat(player.x) # x
# p.putFloat(player.y) # y
# p.putFloat(player.z) # z
# p.putULong(0x00) # unknown
# p.putULong(0x00) # unknown
# p.putULong(0x00) # unknown
# p.putULong(0x00) # unknown
# p.putULong(player.o + 30) # orientation
# p.putULong(0x00) # unknown
p = Packet()
p.putByte(0x0a) # opcode
p.putByte(0x32) # length
p.putByte(0x00) # unknown
p.putByte(0x00) # unknown
p.putByte(0x00) # unknown
p.putByte(plid) # player index
p.putShort(player.oid) # object id
p.putByte(0x00) # unknown
p.putByte(0x01) # 1 -> stand still, 4 -> death, 3 -> little move
p.putULong(model) # model
p.putFloat(0x00) # x
p.putFloat(player.y) # y
p.putFloat(player.z) # z
p.putULong(0x00) # unknown
p.putULong(0x00) # unknown
p.putULong(0x00) # unknown
p.putULong(0x00) # unknown
p.putULong((player.o + 30) % 360) # orientation
p.putULong(0x00) # unknown
p.putByte(0x0a) # opcode
p.putByte(0x12) # length
p.putByte(0x00) # unknown
p.putByte(0x00) # unknown
p.putByte(0x00) # unknown
p.putByte(plid) # player index
p.putShort(player.oid) # object id
p.putByte(0x00) # unknown
p.putByte(0x00) # 1 -> stand still, 4 -> death, 3 -> little move
p.putULong(model) # model
p.putULong(0x00)
p.putByte(0x00)
p.putByte(0x00)
await self.broadcast_gamedata(p.getvalue(), acknow=False)
#await self.broadcast_gamedata(bytes.fromhex("0a32000000" + plix + struct.pack("<B", objid).hex() + "000001" + model + "000000ff00000000000000000000000000000000000000000000000000000000000000000000000a12000000" + plix + struct.pack("<B", objid).hex() + "0000" + model + "020000000000000000"), acknow=False)
def new_oid(self):
a = self._nextid
self._nextid += 1
return a
def get_player_index(self, player):
for iplayer in range(len(self.players)):
if self.players[iplayer] == player:
return iplayer
return False
def get_player_by_index(self, playerindex):
return self.players[playerindex]
if __name__ == '__main__':
server = Server(name="giantsd", maxplayers=20, register=False, teams=Teams.MvM, map=Map("Three Way Island - Canyons.gck")) #, map=Map("Three Way Island - Canyons.gck"))
server.load_plugins()
loop = asyncio.get_event_loop()
listen = loop.create_datagram_endpoint(lambda: Netserver(server), local_addr=(server.listen_ip, server.listen_port))
transport, protocol = loop.run_until_complete(listen)
loop.create_task(server.ask_command())
try:
loop.run_forever()
except KeyboardInterrupt:
pass
transport.close()
loop.close()