Add framework for entity serialization
This commit is contained in:
parent
d400883be1
commit
61e15cff47
58
Jormungand.py
Normal file
58
Jormungand.py
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
import sys
|
||||||
|
from collections import defaultdict
|
||||||
|
from util.Util import logout
|
||||||
|
|
||||||
|
class Jormungand():
|
||||||
|
def __init__(self, argv):
|
||||||
|
command = argv[1] if len(argv) >= 2 else "help"
|
||||||
|
registry = defaultdict(lambda: Jormungand.help, {
|
||||||
|
"help": Jormungand.help,
|
||||||
|
"run": Jormungand.run,
|
||||||
|
"send": Jormungand.send
|
||||||
|
})
|
||||||
|
exec_func = registry[command]
|
||||||
|
self.execute = lambda: exec_func(argv)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def help(argv):
|
||||||
|
print("Hello, world!")
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def run(argv):
|
||||||
|
if len(argv) < 3:
|
||||||
|
print("run requires a port")
|
||||||
|
sys.exit(-1)
|
||||||
|
port = int(argv[2])
|
||||||
|
from world.Server import Server
|
||||||
|
server = Server(port)
|
||||||
|
logout("Launching server on port {}".format(port), "Jormungand run")
|
||||||
|
server.run(argv[3:])
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def send(argv):
|
||||||
|
if len(argv) < 4:
|
||||||
|
print("send requires a target and payload")
|
||||||
|
sys.exit(-1)
|
||||||
|
address = argv[2]
|
||||||
|
import zmq, json, pygame
|
||||||
|
pygame.display.set_mode((1,1), pygame.NOFRAME)
|
||||||
|
context = zmq.Context()
|
||||||
|
socket = context.socket(zmq.REQ)
|
||||||
|
socket.connect("tcp://{}".format(address))
|
||||||
|
for i in range(3, len(argv)):
|
||||||
|
if argv[i] == "-ball":
|
||||||
|
from entity.dummy.DebugBall import DebugBall
|
||||||
|
e = DebugBall()
|
||||||
|
s = json.dumps(e.serialize())
|
||||||
|
print("Sending {}".format(str(e)))
|
||||||
|
for i in range(100):
|
||||||
|
socket.send_string(s)
|
||||||
|
message = socket.recv()
|
||||||
|
print("Reply: {}".format(message))
|
||||||
|
|
||||||
|
def main():
|
||||||
|
j = Jormungand(sys.argv)
|
||||||
|
j.execute()
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
BIN
assets/ball64.png
Normal file
BIN
assets/ball64.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 632 B |
BIN
assets/tube.png
Normal file
BIN
assets/tube.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.0 KiB |
83
entity/Entity.py
Normal file
83
entity/Entity.py
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
import sys
|
||||||
|
from random import randrange
|
||||||
|
|
||||||
|
class Entity(object):
|
||||||
|
"""
|
||||||
|
An Entity is something that exists in the Fishtank entities list. The Entity class provides
|
||||||
|
some basic structure to the behavior of entities, including position and velocity,
|
||||||
|
serialization, and update and draw. Each entity has a random 8-digit hex id for identification
|
||||||
|
purposes.
|
||||||
|
"""
|
||||||
|
def __init__(self):
|
||||||
|
self.fishtank = None
|
||||||
|
self.id = randrange(16**8)
|
||||||
|
self.x = 0
|
||||||
|
self.y = 0
|
||||||
|
self.z = 0
|
||||||
|
self.vx = 0
|
||||||
|
self.vy = 0
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "[{}#{:8x} p{},{} z{} v{},{}]".format(type(self).__name__,
|
||||||
|
self.id, self.x, self.y, self.z, self.vx, self.vy)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "[{}#{:8x}]".format(type(self).__name__, self.id)
|
||||||
|
|
||||||
|
def serialize(self):
|
||||||
|
"""
|
||||||
|
Returns a JSON-compatbible representation of the current state of this entity.
|
||||||
|
Subclasses should override this method, call it from their superclass, then update the
|
||||||
|
returned representation with its own information, including the subclass's module and
|
||||||
|
class and any idiomatic fields of that class.
|
||||||
|
Output: serialized representation of this entity
|
||||||
|
"""
|
||||||
|
return {
|
||||||
|
"module":"entity.Entity",
|
||||||
|
"class":"Entity",
|
||||||
|
"id":"{:8x}".format(self.id),
|
||||||
|
"p":[self.x,self.y],
|
||||||
|
"z":self.z,
|
||||||
|
"v":[self.vx,self.vy]
|
||||||
|
}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def deserialize(serial):
|
||||||
|
"""
|
||||||
|
Reconstructs an Entity from a serialized representation as returned by Entity.serialize().
|
||||||
|
Subclasses should reimplement this method with e as their own class.
|
||||||
|
Input: serial, a serialized representation of an entity
|
||||||
|
Output: an entity reproducing the state represented in serial
|
||||||
|
"""
|
||||||
|
e = Entity()
|
||||||
|
return Entity.rebuild(e, serial)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def rebuild(e, serial):
|
||||||
|
"""
|
||||||
|
Helper function for Entity.deserialize().
|
||||||
|
Subclasses should override this method, call it from their superclass, then add
|
||||||
|
deserialization for their idiomatic fields.
|
||||||
|
Input: e, a newly initialized entity
|
||||||
|
serial, a serialized representation of an entity as passed to deserialize()
|
||||||
|
Output: an entity reproducing the state represented in serial
|
||||||
|
"""
|
||||||
|
e.id = int(serial['id'], 16)
|
||||||
|
e.x, e.y = serial['p']
|
||||||
|
e.z = serial['z']
|
||||||
|
e.vx, e.vy = serial['v']
|
||||||
|
return e
|
||||||
|
|
||||||
|
def update(self, delta):
|
||||||
|
"""
|
||||||
|
Updates this entity during the update pass of the game loop.
|
||||||
|
Input: delta, the number of seconds since the last tick
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def draw(self, screen):
|
||||||
|
"""
|
||||||
|
Draws this entity during the draw pass of the game loop.
|
||||||
|
Input: screen, a Surface object to draw this entity on.
|
||||||
|
"""
|
||||||
|
pass
|
32
entity/Tube.py
Normal file
32
entity/Tube.py
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import sys
|
||||||
|
from entity.Entity import Entity
|
||||||
|
from util.Util import logout, load_image
|
||||||
|
|
||||||
|
class Tube(Entity):
|
||||||
|
def __init__(self, network_gate):
|
||||||
|
Entity.__init__(self)
|
||||||
|
self.gate = network_gate
|
||||||
|
self.inbox = []
|
||||||
|
self.texture = load_image("tube.png")
|
||||||
|
self.x, self.y = 200, 200
|
||||||
|
self.z = -1
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "[Tube gate={} inbox={}]".format(repr(self.gate), repr(self.inbox))
|
||||||
|
|
||||||
|
def accept(self, entity):
|
||||||
|
self.fishtank.remove_entity(entity)
|
||||||
|
self.inbox.append(entity)
|
||||||
|
logout("Accepted: {}".format(str(entity), "Tube#{}".format(self.id)))
|
||||||
|
|
||||||
|
def update(self, delta):
|
||||||
|
Entity.update(self, delta)
|
||||||
|
if self.inbox:
|
||||||
|
entity = self.inbox.pop(0)
|
||||||
|
self.gate.transmit(entity.serialize())
|
||||||
|
|
||||||
|
def draw(self, screen):
|
||||||
|
Entity.draw(self, screen)
|
||||||
|
rect = self.texture.get_rect()
|
||||||
|
rect.center = (int(self.x), int(self.y))
|
||||||
|
screen.blit(self.texture, rect)
|
62
entity/dummy/DebugBall.py
Normal file
62
entity/dummy/DebugBall.py
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
import sys
|
||||||
|
import random
|
||||||
|
import pygame
|
||||||
|
from entity.Entity import Entity
|
||||||
|
from entity.Tube import Tube
|
||||||
|
from util.Util import load_image
|
||||||
|
|
||||||
|
class DebugBall(Entity):
|
||||||
|
"""
|
||||||
|
A debug entity that bounces around and jumps through network tubes.
|
||||||
|
"""
|
||||||
|
def __init__(self):
|
||||||
|
Entity.__init__(self)
|
||||||
|
self.vx = 32
|
||||||
|
self.vy = 128
|
||||||
|
self.dummy = "DUMMY"
|
||||||
|
self.texture = load_image("ball64.png")
|
||||||
|
self.texture = pygame.transform.smoothscale(self.texture, (16,16))
|
||||||
|
self.rect = pygame.Rect(0, 0, 16, 16)
|
||||||
|
|
||||||
|
def serialize(self):
|
||||||
|
e = Entity.serialize(self)
|
||||||
|
e.update({
|
||||||
|
"module":"entity.dummy.DebugBall",
|
||||||
|
"class":"DebugBall",
|
||||||
|
"dummy":self.dummy
|
||||||
|
})
|
||||||
|
return e
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def deserialize(serial):
|
||||||
|
e = DebugBall()
|
||||||
|
return DebugBall.rebuild(e, serial)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def rebuild(e, serial):
|
||||||
|
Entity.rebuild(e, serial)
|
||||||
|
e.dummy = serial["dummy"]
|
||||||
|
return e
|
||||||
|
|
||||||
|
def update(self, delta):
|
||||||
|
Entity.update(self, delta)
|
||||||
|
self.x += self.vx * delta
|
||||||
|
self.y += self.vy * delta
|
||||||
|
if self.x < 0 or self.x > self.fishtank.size[0]:
|
||||||
|
self.vx = -self.vx
|
||||||
|
self.x = 0 if self.x < 0 else self.fishtank.size[0] if self.x > self.fishtank.size[0] else self.x
|
||||||
|
if self.y < 0 or self.y > self.fishtank.size[1]:
|
||||||
|
self.vy = -self.vy
|
||||||
|
self.y = 0 if self.y < 0 else self.fishtank.size[1] if self.y > self.fishtank.size[1] else self.y
|
||||||
|
|
||||||
|
for entity in self.fishtank.entities:
|
||||||
|
if type(entity) is Tube and abs(self.x - entity.x) < 64 and abs(self.y - entity.y) < 64:
|
||||||
|
entity.accept(self)
|
||||||
|
break
|
||||||
|
|
||||||
|
def draw(self, screen):
|
||||||
|
Entity.draw(self, screen)
|
||||||
|
#rect = self.texture.get_rect()
|
||||||
|
self.rect.center = (int(self.x), int(self.y))
|
||||||
|
screen.blit(self.texture, self.rect)
|
||||||
|
|
@ -5,37 +5,37 @@ from entity.Tube import Tube
|
|||||||
class DebugJumper(Entity):
|
class DebugJumper(Entity):
|
||||||
def __init__(self, message):
|
def __init__(self, message):
|
||||||
Entity.__init__(self)
|
Entity.__init__(self)
|
||||||
self._message = message
|
self.message = message
|
||||||
self._counter = 0
|
self.counter = 0
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "[DebugJumper message='{}' counter={}]".format(self._message, self._counter)
|
return "[DebugJumper @{},{} message='{}' counter={}]".format(self.x, self.y, self.message, self.counter)
|
||||||
|
|
||||||
def serialize(self):
|
def serialize(self):
|
||||||
sup = Entity.serialize(self)
|
sup = Entity.serialize(self)
|
||||||
sup.update({
|
sup.update({
|
||||||
"module":"entity.dummy.DebugJumper",
|
"module":"entity.dummy.DebugJumper",
|
||||||
"class":"DebugJumper",
|
"class":"DebugJumper",
|
||||||
"message":self._message,
|
"message":self.message,
|
||||||
"counter":self._counter
|
"counter":self.counter
|
||||||
})
|
})
|
||||||
return sup
|
return sup
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def deserialize(serial):
|
def deserialize(serial):
|
||||||
e = DebugJumper(serial["message"])
|
e = DebugJumper(serial["message"])
|
||||||
e._counter = serial["counter"]
|
e.counter = serial["counter"]
|
||||||
return e
|
return e
|
||||||
|
|
||||||
def update(self):
|
def update(self, delta):
|
||||||
Entity.update(self)
|
Entity.update(self)
|
||||||
self._counter += 1
|
self.counter += 1
|
||||||
if self._counter % 5 == 0:
|
if self.counter % 5 == 0:
|
||||||
for entity in self._fishtank._entities:
|
for entity in self.fishtank.entities:
|
||||||
if type(entity) is Tube:
|
if type(entity) is Tube:
|
||||||
entity.accept(self)
|
entity.accept(self)
|
||||||
break
|
break
|
||||||
|
|
||||||
def draw(self):
|
def draw(self, screen):
|
||||||
Entity.draw(self)
|
Entity.draw(self)
|
||||||
sys.stdout.write(self._message + " ({})\n".format(self._counter))
|
sys.stdout.write(self.message + " ({})\n".format(self.counter))
|
@ -1,43 +0,0 @@
|
|||||||
import sys
|
|
||||||
import time
|
|
||||||
import json
|
|
||||||
|
|
||||||
class Fishtank():
|
|
||||||
def __init__(self, recv_queue):
|
|
||||||
self._entities = []
|
|
||||||
self._to_remove = []
|
|
||||||
self._recv_queue = recv_queue
|
|
||||||
|
|
||||||
def run(self):
|
|
||||||
while True:
|
|
||||||
# Delete flagged entities
|
|
||||||
if self._to_remove:
|
|
||||||
for entity in self._to_remove:
|
|
||||||
self._entities.remove(entity)
|
|
||||||
self._to_remove = []
|
|
||||||
# Update and draw
|
|
||||||
for entity in self._entities:
|
|
||||||
entity.update()
|
|
||||||
for entity in self._entities:
|
|
||||||
entity.draw()
|
|
||||||
#time.sleep(1)
|
|
||||||
# Intake queue
|
|
||||||
if not self._recv_queue.empty():
|
|
||||||
serial = self._recv_queue.get(False)
|
|
||||||
sys.stdout.write("Fishtank dequeued a {}\n".format(serial["class"]))
|
|
||||||
mod = __import__(serial["module"], fromlist=[serial["class"]])
|
|
||||||
klass = getattr(mod, serial["class"])
|
|
||||||
e = klass.deserialize(serial)
|
|
||||||
self.add_entity(e)
|
|
||||||
|
|
||||||
def add_entity(self, entity):
|
|
||||||
entity._fishtank = self
|
|
||||||
self._entities.append(entity)
|
|
||||||
sys.stdout.write("Added: {}\n".format(repr(entity)))
|
|
||||||
|
|
||||||
def remove_entity(self, entity):
|
|
||||||
if entity not in self._entities:
|
|
||||||
sys.stderr.write(
|
|
||||||
"WARN: remove called for entity '{}', but it isn't in the eneityt list\n".format(entity.__name__))
|
|
||||||
return
|
|
||||||
self._to_remove.append(entity)
|
|
@ -1,25 +0,0 @@
|
|||||||
import sys
|
|
||||||
|
|
||||||
class Entity(object):
|
|
||||||
def __init__(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return "[Entity]"
|
|
||||||
|
|
||||||
def serialize(self):
|
|
||||||
return {
|
|
||||||
"module":"entity.Entity",
|
|
||||||
"class":"Entity"
|
|
||||||
}
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def deserialize(serial):
|
|
||||||
e = Entity()
|
|
||||||
return e
|
|
||||||
|
|
||||||
def update(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def draw(self):
|
|
||||||
pass
|
|
@ -1,22 +0,0 @@
|
|||||||
import sys
|
|
||||||
from entity.Entity import Entity
|
|
||||||
|
|
||||||
class Tube(Entity):
|
|
||||||
def __init__(self, network_gate):
|
|
||||||
Entity.__init__(self)
|
|
||||||
self._gate = network_gate
|
|
||||||
self._inbox = []
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return "[Tube gate={} inbox={}]".format(repr(self._gate), repr(self._inbox))
|
|
||||||
|
|
||||||
def accept(self, entity):
|
|
||||||
self._fishtank.remove_entity(entity)
|
|
||||||
self._inbox.append(entity)
|
|
||||||
|
|
||||||
def update(self):
|
|
||||||
Entity.update(self)
|
|
||||||
if self._inbox:
|
|
||||||
entity = self._inbox.pop(0)
|
|
||||||
sys.stdout.write("Sending: {}\n".format(repr(entity)))
|
|
||||||
self._gate.transmit(entity.serialize())
|
|
@ -1,19 +0,0 @@
|
|||||||
import sys
|
|
||||||
from entity.Entity import Entity
|
|
||||||
|
|
||||||
class DebugWaiter(Entity):
|
|
||||||
def __init__(self, message):
|
|
||||||
Entity.__init__(self)
|
|
||||||
self._message = message
|
|
||||||
self._counter = 0
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return "[DebugWaiter message='{}' counter={}]".format(self._message, self._counter)
|
|
||||||
|
|
||||||
def update(self):
|
|
||||||
Entity.update(self)
|
|
||||||
self._counter += 1
|
|
||||||
|
|
||||||
def draw(self):
|
|
||||||
Entity.draw(self)
|
|
||||||
sys.stdout.write(self._message + " ({})\n".format(self._counter))
|
|
@ -1,19 +0,0 @@
|
|||||||
import sys
|
|
||||||
import zmq
|
|
||||||
import json
|
|
||||||
|
|
||||||
class NetworkGate():
|
|
||||||
def __init__(self, address, port):
|
|
||||||
self._address = address
|
|
||||||
self._port = port
|
|
||||||
context = zmq.Context()
|
|
||||||
self._socket = context.socket(zmq.REQ)
|
|
||||||
self._socket.connect("tcp://{}:{}".format(address, port))
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return "[NetworkGate {}:{}]".format(self._address, self._port)
|
|
||||||
|
|
||||||
def transmit(self, serial):
|
|
||||||
s = json.dumps(serial)
|
|
||||||
self._socket.send_string(s)
|
|
||||||
message = self._socket.recv()
|
|
@ -1,54 +0,0 @@
|
|||||||
import sys
|
|
||||||
import zmq
|
|
||||||
import json
|
|
||||||
import time
|
|
||||||
from multiprocessing import Process, Queue
|
|
||||||
from Fishtank import Fishtank
|
|
||||||
|
|
||||||
def socket_listener(port, recv_queue):
|
|
||||||
sys.stdout.write("Socket listener starting...\n")
|
|
||||||
context = zmq.Context()
|
|
||||||
socket = context.socket(zmq.REP)
|
|
||||||
socket.bind("tcp://*:{}".format(port))
|
|
||||||
|
|
||||||
while True:
|
|
||||||
message = socket.recv()
|
|
||||||
response = b"Undefined response"
|
|
||||||
try:
|
|
||||||
serial = json.loads(message)
|
|
||||||
if "class" in serial:
|
|
||||||
sys.stdout.write("Listener received a {}\n".format(serial["class"]))
|
|
||||||
recv_queue.put(serial, False)
|
|
||||||
response = b"Received"
|
|
||||||
except:
|
|
||||||
response = b"Error"
|
|
||||||
socket.send(response)
|
|
||||||
|
|
||||||
def main():
|
|
||||||
port = int(sys.argv[1])
|
|
||||||
sys.stdout.write("Launching on port {}\n".format(port))
|
|
||||||
# Spawn the socket thread
|
|
||||||
q = Queue()
|
|
||||||
socket_proc = Process(target=socket_listener, args=(port,q))
|
|
||||||
socket_proc.start()
|
|
||||||
sys.stdout.write("Socket thread started\n")
|
|
||||||
time.sleep(1)
|
|
||||||
# Build the world
|
|
||||||
fishtank = Fishtank(q)
|
|
||||||
for i in range(1, len(sys.argv)):
|
|
||||||
if sys.argv[i] == "-w":
|
|
||||||
from entity.dummy.DebugWaiter import DebugWaiter
|
|
||||||
fishtank.add_entity(DebugWaiter("DebugWaiter"))
|
|
||||||
if sys.argv[i] == "-j":
|
|
||||||
from entity.dummy.DebugJumper import DebugJumper
|
|
||||||
fishtank.add_entity(DebugJumper("DebugJumper"))
|
|
||||||
if sys.argv[i] == "-t":
|
|
||||||
pipe_port = int(sys.argv[i+1])
|
|
||||||
from network.NetworkGate import NetworkGate
|
|
||||||
network_gate = NetworkGate("localhost", pipe_port)
|
|
||||||
from entity.Tube import Tube
|
|
||||||
fishtank.add_entity(Tube(network_gate))
|
|
||||||
fishtank.run()
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
26
util/Util.py
Normal file
26
util/Util.py
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import datetime
|
||||||
|
from os.path import join
|
||||||
|
import sys
|
||||||
|
import pygame
|
||||||
|
|
||||||
|
def logout(s, tag=""):
|
||||||
|
sys.stdout.write("[{:%Y-%m-%d %H:%M:%S} {}] {}\n".format(datetime.datetime.now(), tag, s))
|
||||||
|
|
||||||
|
def logerr(s, tag=""):
|
||||||
|
sys.stderr.write("[{:%Y-%m-%d %H:%M:%S} {}] {}\n".format(datetime.datetime.now(), tag, s))
|
||||||
|
|
||||||
|
def load_image(filename):
|
||||||
|
path = join("assets", filename)
|
||||||
|
try:
|
||||||
|
image = pygame.image.load(path)
|
||||||
|
except:
|
||||||
|
logerr("ERROR: Image '{}' not found".format(filename), "load_image")
|
||||||
|
sys.exit(-1)
|
||||||
|
#try:
|
||||||
|
if image.get_alpha() is None:
|
||||||
|
image = image.convert()
|
||||||
|
else:
|
||||||
|
image = image.convert_alpha()
|
||||||
|
#except:
|
||||||
|
# logerr("ERROR: Image '{}' not converted".format(filename))
|
||||||
|
return image
|
71
world/Fishtank.py
Normal file
71
world/Fishtank.py
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
import sys
|
||||||
|
import time
|
||||||
|
import json
|
||||||
|
import pygame
|
||||||
|
from util.Util import logout, logerr
|
||||||
|
|
||||||
|
class Fishtank():
|
||||||
|
def __init__(self, recv_queue):
|
||||||
|
self.entities = []
|
||||||
|
self.to_remove = []
|
||||||
|
self.recv_queue = recv_queue
|
||||||
|
|
||||||
|
self.size = (480, 360)
|
||||||
|
self.screen = pygame.display.set_mode(self.size)
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
"""Begins the game loop. Does not return."""
|
||||||
|
clock = pygame.time.Clock()
|
||||||
|
|
||||||
|
while True:
|
||||||
|
# Upkeep
|
||||||
|
milli = clock.tick(60) / 1000
|
||||||
|
|
||||||
|
for event in pygame.event.get():
|
||||||
|
if event.type == pygame.QUIT:
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
# Delete flagged entities
|
||||||
|
if self.to_remove:
|
||||||
|
for entity in self.to_remove:
|
||||||
|
self.entities.remove(entity)
|
||||||
|
self.to_remove = []
|
||||||
|
|
||||||
|
# Update
|
||||||
|
for entity in self.entities:
|
||||||
|
entity.update(milli)
|
||||||
|
|
||||||
|
# Intake queue
|
||||||
|
if not self.recv_queue.empty():
|
||||||
|
serial = self.recv_queue.get(False)
|
||||||
|
logout("Dequeued: {}".format(serial["class"]), "Fishtank")
|
||||||
|
mod = __import__(serial["module"], fromlist=[serial["class"]])
|
||||||
|
klass = getattr(mod, serial["class"])
|
||||||
|
e = klass.deserialize(serial)
|
||||||
|
self.add_entity(e)
|
||||||
|
|
||||||
|
# Draw
|
||||||
|
self.screen.fill((100, 149, 237))
|
||||||
|
for entity in sorted(self.entities, key=lambda e:e.z):
|
||||||
|
entity.draw(self.screen)
|
||||||
|
pygame.display.flip()
|
||||||
|
|
||||||
|
def add_entity(self, entity):
|
||||||
|
"""
|
||||||
|
Adds an entity to the entity list and sets its fishtank property to this.
|
||||||
|
Input: entity, the entity to add
|
||||||
|
"""
|
||||||
|
entity.fishtank = self
|
||||||
|
self.entities.append(entity)
|
||||||
|
logout("Added: {}".format(repr(entity)), "Fishtank")
|
||||||
|
|
||||||
|
def remove_entity(self, entity):
|
||||||
|
"""
|
||||||
|
Marks an entity to be removed before the next update pass.
|
||||||
|
Input: entity, the entity to remove
|
||||||
|
"""
|
||||||
|
if entity not in self.entities:
|
||||||
|
logerr("WARN: remove called for entity '{}',"\
|
||||||
|
"but it isn't in the entity list".format(entity.__name__), "Fishtank")
|
||||||
|
return
|
||||||
|
self.to_remove.append(entity)
|
21
world/NetworkGate.py
Normal file
21
world/NetworkGate.py
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import sys
|
||||||
|
import zmq
|
||||||
|
import json
|
||||||
|
from util.Util import logout
|
||||||
|
|
||||||
|
class NetworkGate():
|
||||||
|
def __init__(self, address, port):
|
||||||
|
self.address = address
|
||||||
|
self.port = port
|
||||||
|
context = zmq.Context()
|
||||||
|
self.socket = context.socket(zmq.REQ)
|
||||||
|
self.socket.connect("tcp://{}:{}".format(address, port))
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "[NetworkGate {}:{}]".format(self.address, self.port)
|
||||||
|
|
||||||
|
def transmit(self, serial):
|
||||||
|
s = json.dumps(serial)
|
||||||
|
logout("Sending: {}".format(serial["class"]))
|
||||||
|
self.socket.send_string(s)
|
||||||
|
message = self.socket.recv()
|
54
world/Server.py
Normal file
54
world/Server.py
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
import zmq
|
||||||
|
import json
|
||||||
|
import time
|
||||||
|
from multiprocessing import Process, Queue
|
||||||
|
from world.Fishtank import Fishtank
|
||||||
|
from util.Util import logout
|
||||||
|
|
||||||
|
class Server(object):
|
||||||
|
def __init__(self, recv_port):
|
||||||
|
self.recv_port = recv_port
|
||||||
|
self.recv_queue = Queue()
|
||||||
|
self.recv_proc = Process(
|
||||||
|
target=self.socket_listener,
|
||||||
|
args=(self.recv_port, self.recv_queue))
|
||||||
|
|
||||||
|
def socket_listener(self, port, recv_queue):
|
||||||
|
logout("Socket thread starting", "Server.socket_listener")
|
||||||
|
context = zmq.Context()
|
||||||
|
socket = context.socket(zmq.REP)
|
||||||
|
socket.bind("tcp://*:{}".format(port))
|
||||||
|
|
||||||
|
while True:
|
||||||
|
message = socket.recv()
|
||||||
|
response = b"Undefined response"
|
||||||
|
try:
|
||||||
|
serial = json.loads(message)
|
||||||
|
if "class" in serial:
|
||||||
|
logout("Received: {}".format(serial["class"]), "Server.socket_listener")
|
||||||
|
recv_queue.put(serial, False)
|
||||||
|
response = b"Received"
|
||||||
|
except:
|
||||||
|
response = b"Error"
|
||||||
|
socket.send(response)
|
||||||
|
|
||||||
|
def run(self, argv):
|
||||||
|
# Launch the recv process
|
||||||
|
self.recv_proc.start()
|
||||||
|
logout("Socket thread launched", "Server")
|
||||||
|
time.sleep(0.2)
|
||||||
|
|
||||||
|
# Build the world
|
||||||
|
fishtank = Fishtank(self.recv_queue)
|
||||||
|
|
||||||
|
for i in range(len(argv)):
|
||||||
|
if argv[i] == "-ball":
|
||||||
|
from entity.dummy.DebugBall import DebugBall
|
||||||
|
fishtank.add_entity(DebugBall())
|
||||||
|
if argv[i] == "-tube":
|
||||||
|
tube_port = int(argv[i+1])
|
||||||
|
from world.NetworkGate import NetworkGate
|
||||||
|
network_gate = NetworkGate("localhost", tube_port)
|
||||||
|
from entity.Tube import Tube
|
||||||
|
fishtank.add_entity(Tube(network_gate))
|
||||||
|
fishtank.run()
|
0
world/__init__.py
Normal file
0
world/__init__.py
Normal file
Loading…
Reference in New Issue
Block a user