forked from notbaab/TiledPlatformer
-
Notifications
You must be signed in to change notification settings - Fork 0
/
game.py
337 lines (291 loc) · 12 KB
/
game.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
import pygame
import sys
import ipdb
from pygame.locals import *
import world as wd
import engine as eng
import socket
if (sys.version_info > (3, 0)):
import pickle as pickle
else:
import cPickle as pickle
import os
import json
network_settings = json.load(open('network_settings.json'))
json_data = open('master_settings.json')
config = json.load(json_data)
# TODO: Maybe it's time to move away from the socket del? That will also require moving off pickling
SOCKET_DEL = config['package_delimeter'].encode('utf-8')
loc = []
FPS = pygame.time.Clock()
TICK = int(config['FPS_TICK'])
GRID_SPACE = [int(config['grid_space'][0]), int(config['grid_space'][1])]
# DISPLAY_SIZE = [600, 600]
DISPLAY_SIZE = {"x": int(config['display_size'][0]), "y": int(config['display_size'][1])}
BEZZEL_SIZE = [30, 30]
DEBUG_CLASSES = []
# DEBUG_CLASSES = [wd.SimpleScenery, wd.Player]
# TODO: have a platformer game class that has all the similar components of the render and
# master node, and inherit from that?
class MasterPlatformer(object):
"""Class for the platformer head node"""
def __init__(self, localhosts=1, ip_file=None):
global config, network_settings
super(MasterPlatformer, self).__init__()
os.environ['SDL_VIDEO_WINDOW_POS'] = "%d,%d" % (0, 0) # move window to upper left corner
pygame.init()
self.game_objects = {}
self.window = pygame.display.set_mode((60, 60))
self.engine = eng.Engine()
self.added = [] # list keeping track of which objects are added
self.deleted = [] # list keeping track of the ids of objects that are deleted
self.game_objects = self.load_map()
# remove none debugin classes if that is what we are doing
if DEBUG_CLASSES:
new_game_obj = self.game_objects.copy()
for obj in self.game_objects.values():
if type(obj) not in DEBUG_CLASSES:
print(str(type(obj)))
del new_game_obj[obj.id]
self.game_objects = new_game_obj.copy()
if network_settings['localhost'] == "True":
# Testing one local node, read from the setting to find out which tile we are testing and read
# move the player to the correct place
ip_list = ['localhost']
# spawn each player in the corner of the screen
left_side = DISPLAY_SIZE["x"] * int(network_settings["x"])
top_side = DISPLAY_SIZE["y"] * int(network_settings["y"])
game_dict = self.structured_list() #
player1 = game_dict['Player'][0]
player2 = game_dict['Player'][1]
player1.rect.x = left_side + 1000
player2.rect.x = left_side + 900
player1.rect.y = top_side + 200
player2.rect.y = top_side + 200
print(player1.rect)
print(player2.rect)
else:
# ip_list
self.ip_list = []
ips = open('tile-hosts.txt', 'r')
address = ips.readline().strip()
ip_list = []
while address:
ip_list.append(address)
address = ips.readline().strip()
# build the initial data packet
send_struct = {'game_obj': []}
for game_obj in self.game_objects.values():
send_dict = {"rect": [game_obj.rect.x, game_obj.rect.y, game_obj.rect.width,
game_obj.rect.height], "id": game_obj.id,
"constructor": type(game_obj).__name__}
if hasattr(game_obj, "sprite_sheet"):
send_dict["sprite_sheet"] = game_obj.sprite_sheet
send_struct['game_obj'].append(send_dict)
data = pickle.dumps(send_struct, pickle.HIGHEST_PROTOCOL) + '*ET*'.encode('utf-8')
self.socket_list = []
for ip in ip_list:
self.socket_list.append(socket.socket(socket.AF_INET, socket.SOCK_STREAM))
self.socket_list[-1].connect((ip, 2000))
self.socket_list[-1].sendall(data)
for node in self.socket_list:
self.get_whole_packet(node)
self.state = 'load'
def run(self):
while True:
if self.state == 'play':
data, self.state = self.play_frame()
elif self.state == 'load':
data, self.state = self.load()
else:
ipdb.set_trace()
FPS.tick(TICK)
# print(FPS.get_fps())
def load(self):
send_struct = {'state': 'load'}
# clients handle the load state so wait for their response and play the game
return self.serialize_and_sync(send_struct)
def play_frame(self):
game_dict = self.structured_list() # Structure the game object list to manage easier. n time should be fast
player1 = game_dict['Player'][0]
player2 = game_dict['Player'][1]
for event in pygame.event.get():
if event.type == pygame.QUIT:
self.quit()
sys.exit()
elif event.type == KEYDOWN:
if event.key == K_ESCAPE:
self.quit()
sys.exit()
if event.key == K_LEFT:
player1.move_left()
player1.escape(1)
elif event.key == K_RIGHT:
player1.move_right()
player1.escape(2)
elif event.key == K_UP:
player1.jump()
elif event.key == K_t:
player1.throw_data()
elif event.key == K_a:
player2.move_left()
player2.escape(1)
elif event.key == K_d:
player2.move_right()
player2.escape(2)
elif event.key == K_w:
player2.jump()
elif event.key == K_SPACE:
player1.interact(self.game_objects.values()) # TODO: We are passing in way to much data here, fix it.
elif event.type == KEYUP:
if event.key == K_LEFT:
player1.stop_left()
elif event.key == K_RIGHT:
player1.stop_right()
elif event.key == K_a:
player2.stop_left()
elif event.key == K_d:
player2.stop_right()
self.engine.physics_simulation(self.game_objects.values(), [wd.SimpleScenery])
self.engine.map_attribute_flat(self.game_objects.values(), 'update')
self.engine.map_attribute_flat(self.game_objects.values(), 'animate')
# update the AI after the players have been updated
self.engine.map_attribute_flat(game_dict['AI'], 'check_for_leader', game_dict['Player'])
# update meetings/traps
self.engine.map_attribute_flat(game_dict['Meeting'], 'check_player', game_dict['Player'])
# construct packet
send_struct = {'state': 'play', 'deleted_objs': [], 'added_objs': []}
if network_settings['localhost']:
send_struct['localhost'] = self.handle_localhost(game_dict['Player'][0])
# check for objects that have been created and add them to the dict
for game_obj in self.added:
self.game_objects[game_obj.id] = game_obj
send_struct['added_objs'].append({"rect": [game_obj.rect.x, game_obj.rect.y, game_obj.rect.width,
game_obj.rect.height], "id": game_obj.id,
"sprite_sheet": game_obj.sprite_sheet,
"constructor": type(game_obj).__name__})
for game_obj_id in self.deleted:
send_struct['deleted_objs'].append(game_obj_id)
del self.game_objects[game_obj_id]
# clear lists
self.added = []
self.deleted = []
game_objects_packets = [] # accumulator for the build_packet function
self.engine.map_attribute_flat(game_dict['NetworkedObject'], 'build_packet', game_objects_packets)
send_struct['game_objects'] = game_objects_packets
return self.serialize_and_sync(send_struct)
def structured_list(self):
"""take the game object list and return a dict with the keys for static, AI, and player
objects. An object can be added to multiple lists if it is multiple things i.e.
a player is a movable game object"""
ret_dict = {'AI': [], 'StaticObject': [], 'Player': [], 'MovableGameObject': [],
'NetworkedObject': [], 'Meeting': []}
for game_obj in self.game_objects.values():
if isinstance(game_obj, wd.Player):
ret_dict['Player'].append(game_obj)
elif isinstance(game_obj, wd.SimpleScenery):
ret_dict['StaticObject'].append(game_obj)
elif isinstance(game_obj, wd.Follower):
ret_dict['AI'].append(game_obj)
if isinstance(game_obj, wd.MovableGameObject):
ret_dict['MovableGameObject'].append(game_obj)
if isinstance(game_obj, wd.NetworkedObject):
ret_dict['NetworkedObject'].append(game_obj)
if isinstance(game_obj, wd.Meeting):
ret_dict['Meeting'].append(game_obj)
return ret_dict
def handle_localhost(self, follow_player):
"""special function used to handle things like switching the screens when playing on one local host"""
# first, find out which tile player one is in.
tile_x = int(follow_player.rect.centerx / DISPLAY_SIZE['x'])
tile_y = int(follow_player.rect.centery / DISPLAY_SIZE['y'])
return {'x':tile_x, 'y':tile_y}
# print(tile_x)
# print(tile_y)
def add_to_world(self, game_obj):
self.game_objects[game_obj.id] = game_obj
def get_whole_packet(self, sock):
"""ensures that we receive the whole stream of data"""
data = ''.encode('utf-8')
while True:
data += sock.recv(4024)
split = data.split(SOCKET_DEL) # split at newline, as per our custom protocol
if len(split) != 2: # it should be 2 elements big if it got the whole message
pass
else:
x = pickle.loads(split[0])
return x
def serialize_and_sync(self, send_struct):
"""serialize data and send it to the nodes."""
# serialize the data and send
data = pickle.dumps(send_struct, pickle.HIGHEST_PROTOCOL) + '*ET*'.encode('utf-8')
for node in self.socket_list:
node.sendall(data)
return_list = []
for node in self.socket_list:
return_list.append(self.get_whole_packet(node))
# TODO: return real data
return '', 'play'
def quit(self):
data = pickle.dumps({'state': 'kill'}, pickle.HIGHEST_PROTOCOL) + '*ET*'.encode('utf-8')
for node in self.socket_list:
node.sendall(data)
time.sleep(2)
def load_map(self):
global config
"""this function is stupid as shit. I hope you look back at this and feel
bad about how awful you approached this. You deserve to feel bad for writing it
like this."""
# load map
game_objects = {}
map_json = self.engine.parse_json(config['global_map_file'])
asset_json = self.engine.parse_json(config['asset_file'])
# TODO: abstract this parsing to dynamically call the constructor based on the
# attribute (reuse map)
for key in map_json:
print (key)
constructor = getattr(wd, key)
# try:
for obj_dict in map_json[key]:
if key == "Portal":
self.handle_portal(obj_dict, game_objects)
continue
if "tile" in obj_dict:
# tranlate the x and y coordinates
x, y = self.translate_to_tile(obj_dict['tile'][0], int(obj_dict['x']),
obj_dict['tile'][1], int(obj_dict['y']))
else:
print("nope")
x, y = int(obj_dict['x']), int(obj_dict['y'])
if key not in asset_json:
# "invisible object"
if issubclass(constructor, wd.Constructor):
tmp = constructor(x, y, int(obj_dict['width']),
int(obj_dict['height']), game=self)
else:
tmp = constructor(x, y, int(obj_dict['width']),
int(obj_dict['height']))
else:
tmp = constructor(x, y, int(obj_dict['width']),
int(obj_dict['height']), sprite_sheet=asset_json[key])
game_objects[tmp.id] = tmp
# except Exception, e:
# ipdb.set_trace()
print(game_objects)
return game_objects
def handle_portal(self, game_objects):
"""portals are specail objects that need to be created two at a time and
have there own setting structure"""
return
def translate_to_tile(self, tile_x, pos_x, tile_y, pos_y):
x = int(tile_x) * DISPLAY_SIZE['x'] + pos_x
y = int(tile_y) * DISPLAY_SIZE['y'] + pos_y
print(x, y)
return x, y
if __name__ == '__main__':
print(sys.argv)
if len(sys.argv) != 2:
game = MasterPlatformer(localhosts=1, ip_file=False)
else:
game = MasterPlatformer(localhosts=int(sys.argv[1]))
game.run()