comparison server.rb @ 0:1eef88068f9f tip

initial commit of maze game source
author ferencd
date Sun, 15 Sep 2019 11:46:47 +0200
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:1eef88068f9f
1 require 'net/http/server'
2 require 'pp'
3 require 'ellers'
4 require 'wilsons'
5 require 'distance_grid'
6 require 'pg'
7 require 'base62-rb'
8 require 'zlib'
9 require 'wlang'
10 require 'set'
11 require 'descends'
12 require 'pg'
13 require 'database'
14 require 'openssl'
15 require 'game'
16 require 'gs_logger'
17 require 'gamedata'
18
19 #
20 # Starting the App, updating things that need to be
21 #
22 $LOG.info 'gameserver 0.1 starting'
23 Database.initialize
24
25 #
26 # Constants
27 #
28 CONTENT_TYPE = 'Content-Type'
29 MAIN_HTML = 'lab.html'
30 CONTENT_TYPES =
31 {
32 :png => 'image/png',
33 :gif => 'image/gif',
34 :js => 'application/javascript'
35 }
36
37 GAME_TYPE_MAZE = 1
38 GAME_TYPE_ADVENTURE = 2
39 GAME_TYPE_STORY = 3
40
41 #################################################################################################################
42 # Helper classes
43 #################################################################################################################
44
45 #
46 # Used to get some extra numbers
47 #
48 class Integer
49 N_BYTES = [42].pack('i').size
50 N_BITS = N_BYTES * 16
51 MAX = 2 ** (N_BITS - 2) - 1
52 MIN = -MAX - 1
53 end
54
55 #
56 # An extension to the plain string class to provide b62 validations
57 #
58 class String
59 def valid_b62?
60 !!match(/^[[:alnum:]]+$/)
61 end
62
63 def is_i?
64 /\A[-+]?\d+\z/ === self
65 end
66
67 def encrypt(key)
68 cipher = OpenSSL::Cipher::Cipher.new('DES-EDE3-CBC').encrypt
69 cipher.key = Digest::SHA1.hexdigest key
70 s = cipher.update(self) + cipher.final
71 s.unpack('H*')[0].upcase
72 end
73
74 def decrypt(key)
75 cipher = OpenSSL::Cipher::Cipher.new('DES-EDE3-CBC').decrypt
76 cipher.key = Digest::SHA1.hexdigest key
77 s = [self].pack('H*').unpack('C*').pack('c*')
78 cipher.update(s) + cipher.final
79 end
80
81 end
82
83 #
84 # Simple class to NOT to escape HTML code in the wlang renderer
85 #
86 class SimpleHtmlRenderer < WLang::Dialect
87
88 def highlight(buf, fn)
89 var_name = render(fn)
90 var_value = evaluate(var_name)
91 buf << var_value
92 end
93
94 tag '$', :highlight
95 end
96
97
98 #################################################################################################################
99 # Helper methods #
100 #################################################################################################################
101
102 #
103 # Provides a fully completed template for the template engine
104 #
105 def complete_templates(templates)
106 (1..5).each do | v |
107 templates["gotext#{v}".to_sym] = '' unless templates.has_key? "gotext#{v}".to_sym
108 end
109 templates
110 end
111
112 #
113 # Just returns page not found
114 #
115 def page_not_found
116 content_type = {CONTENT_TYPE => 'text/html'}
117 retv = [ '<html><center><strong>404 :-(</strong></center></html>' ]
118 code = 404
119 return code, content_type, retv
120 end
121
122 def internal_server_error
123 content_type = {CONTENT_TYPE => 'text/html'}
124 retv = [ '<html><center><strong>500 :-(</strong></center></html>' ]
125 code = 500
126 return code, content_type, retv
127 end
128
129
130 #
131 # Will create a random room in the grid
132 #
133 def create_room(grid)
134
135 tries = 0
136 can_not_make = false
137 begin
138
139 max_width = [grid.columns/10, 6].max
140 max_height = [grid.rows/10, 6].max
141
142 grid_width = rand(2 .. max_width) + 2
143 grid_height = rand(2 .. max_height) + 2
144
145 x1 = rand(grid.columns - grid_width - 4)
146 x2 = x1 + grid_width
147 y1 = rand(grid.rows - grid_height - 4)
148 y2 = y1 + grid_height
149
150 tries += 1
151 can_not_make = !grid.can_make_room(x1,y1,x2,y2)
152
153 end while can_not_make && tries > 5
154
155 return '' if tries >= 5
156
157 grid.create_room x1, y1, x2, y2
158
159 # Place the door
160 door_x_cell = x1 + grid_width / 2
161 door_y_top_cell = y1
162 door_y_bottom_cell = y1 + 1
163
164 "{r:#{door_y_bottom_cell},c:#{door_x_cell},v:0}, {r:#{door_y_top_cell},c:#{door_x_cell},v:1}"
165 end
166
167 #
168 # Calculates the distance from the goal cell on the given grid towards the cell_to_go
169 # and returns the final_len and final_path
170 #
171 def distance_calculator(goal, grid, cell_to_go, final_len, final_path)
172 another_distances = goal.distances
173 grid.distances = another_distances.path_to(cell_to_go)
174 jsv2 = grid.distances_js_array_path
175 final_len += jsv2.length
176 final_path += jsv2
177 return final_len, final_path
178 end
179
180 #
181 # Generates a path for the given walker
182 #
183 def create_walker_path(grid, r, c)
184 # making a new path from the furthest point from there
185 new_distances = grid[r, c].distances
186
187 # goal will be the cell which is the furthest from the new_start and max_dist the length of it
188 goal, _ = new_distances.max
189 grid.distances = new_distances.path_to(goal)
190
191 jsv = grid.distances_js_array_path
192 final_path = jsv
193 final_len = final_path.length
194
195 # And the goal is that the walker will take a round around the maze
196
197 # Now add in another path to (0,0)
198 final_len, final_path = distance_calculator(goal, grid, grid[0, 0],final_len, final_path)
199 final_len, final_path = distance_calculator(grid[0, 0], grid, grid[0, grid.columns - 1],final_len, final_path)
200 final_len, final_path = distance_calculator(grid[0, grid.columns - 1], grid, grid[grid.rows - 1, grid.columns - 1],final_len, final_path)
201
202 s = ''
203 s << "path_len:#{final_len},r:#{r},c:#{c},p:'#{final_path}'"
204 s
205
206 end
207
208 # will generate a few walking characters
209 def generate_walkers(grid, max_nr)
210 walkers = []
211 return walkers if max_nr == 0
212
213 walker_count = rand(1 .. max_nr + 1)
214 $LOG.info"Generating #{walker_count} walkers\n"
215 #walkers << {:body => 0, :row => 0, :col => 1, :path => "path_len:3,r:0,c:1,p:'EEE'"}
216 #walkers << {:body => 0, :row => 0, :col => 4, :path => "path_len:3,r:0,c:4,p:'WWW'"}
217 dbg_cwa = 0
218 (1..walker_count).each do
219 walker_body = dbg_cwa #rand(3)
220 dbg_cwa = 1 if dbg_cwa == 0
221 walker_row = rand(1 .. grid.rows - 2) + 1
222 walker_col = rand(1 .. grid.columns - 2) + 1
223 walkers <<
224 {
225 :body => walker_body,
226 :row => walker_row,
227 :col => walker_col,
228 :path => create_walker_path(grid,walker_row,walker_col)
229 }
230 end
231 walkers
232 end
233
234
235 #
236 # Will put the loot stuff on the level. food_locations is an array where wealready have placed food
237 #
238 def place_loot(grid, food_locations)
239 loot_js = 'var loot=['
240 max_possible_loot = grid.dead_ends[1].length + grid.dead_ends[2].length + grid.dead_ends[4].length + grid.dead_ends[8].length
241 max_possible_loot = max_possible_loot / 4 + 1
242
243 max_loot_count = $ITEMS.length # 32 items defined in the png_to_js
244
245 loot_count = [rand(max_possible_loot), max_loot_count].min # We want a lot of loot in the maze
246
247 current_loot_count = 0
248 idxs = [1, 2, 4, 8]
249 idxsc = 0
250 current_idx = idxs[idxsc]
251 loot_js_arr = []
252 placed_loots = []
253 current_loot_value = 0
254
255 (1..loot_count).each do |_|
256
257 place_ctr = 0
258 can_place = true
259 begin
260 cell = grid.dead_ends[current_idx][rand (grid.dead_ends[current_idx].length)]
261 place_ctr += 1
262 already_placed_here = placed_loots.select {|loot_loc| loot_loc[:r] == cell.row && loot_loc[:c] == cell.column}
263 here_is_not_fine = already_placed_here.length > 0
264
265 unless here_is_not_fine
266 food_placed_here = food_locations.select {|food_loc| food_loc[:r] == cell.row && food_loc[:c] == cell.column}
267 here_is_not_fine |= food_placed_here.length > 0
268 end
269
270 end while here_is_not_fine && place_ctr < grid.dead_ends[current_idx].length-1
271
272 if place_ctr == grid.dead_ends[current_idx].length-1
273 if already_placed_here.length > 0
274 # try to find the first non empty place in the grid
275 found = false
276 (0..grid.dead_ends[current_idx].length - 1).each do |i|
277 cell = grid.dead_ends[current_idx][i]
278
279 already_placed_here = placed_loots.select {|loot_loc| loot_loc[:r] == cell.row && loot_loc[:c] == cell.column}
280 can_go_here = already_placed_here.length == 0
281 food_placed_here = food_locations.select {|loot_loc| loot_loc[:r] == cell.row && loot_loc[:c] == cell.column}
282 can_go_here &= food_placed_here.length == 0
283
284 if can_go_here
285 found = true
286 break
287 end
288
289 end
290 can_place = false if found
291 end
292 end
293
294 if can_place
295 loot_js_arr << "{r:#{cell.row},c:#{cell.column},t:#{current_loot_count}}" if (cell.row > 0 && cell.column > 0)
296
297 current_loot_value += $ITEMS[current_loot_count][:value]
298 placed_loots << {:r => cell.row, :c => cell.column}
299 else
300 break
301 end
302
303 current_idx = idxs[idxsc]
304
305 idxsc += 1
306 if idxsc == idxs.length
307 idxsc = 0
308 end
309 current_loot_count += 1
310 if current_loot_count == max_loot_count
311 current_loot_count = 0
312 end
313 end
314 loot_js << loot_js_arr.join(',') << "];\n"
315 return loot_js, placed_loots, current_loot_value
316 end
317
318 #
319 # Will put the food on the table ... ie. level
320 #
321 def place_food(grid, level_number, game_type)
322 food_js = 'var food=['
323 max_possible_food = grid.dead_ends[1].length + grid.dead_ends[2].length + grid.dead_ends[4].length + grid.dead_ends[8].length
324 max_possible_food = max_possible_food / 4 + 1
325 food_count = rand(max_possible_food)
326
327 if game_type == GAME_TYPE_STORY
328 if level_number > 55
329 food_count *= 4
330 elsif level_number > 45
331 food_count *= 3
332 elsif level_number > 20
333 food_count *= 2
334 elsif level_number < 10
335 food_count /= 2
336 food_count += 1
337 end
338 end
339
340 max_food_count = 10 # This comes from png_to_js the number of possible food items available
341 current_food_count = 0 # Counts the food items of the max available
342 idxs = [1, 2, 4, 8] # The indexes of the cells, which are "dead ends".
343 idxsc = 0 # Counter for the "dead end" index array
344 current_idx = idxs[idxsc] # The current "dead end" index
345 food_js_arr = [] # Will hold the javascript array for the food stuff
346 placed_foods = [] # Will hold where the food was placed
347
348 (1..food_count).each do |_|
349
350 place_ctr = 0
351 can_place = true
352
353 # Find a place (cell) where we can place the food item
354 begin
355 cell = grid.dead_ends[current_idx][rand (grid.dead_ends[current_idx].length)]
356 place_ctr += 1
357 already_placed_here = placed_foods.select {|food_loc| food_loc[:r] == cell.row && food_loc[:c] == cell.column}
358 end while already_placed_here.length>0 && place_ctr < grid.dead_ends[current_idx].length-1
359
360 if place_ctr == grid.dead_ends[current_idx].length-1
361 # Did not find an "empty dead end" cell to put the food there
362 if already_placed_here.length > 0
363 # try to find the first empty cell in the grid
364 found = false
365 (0..grid.dead_ends[current_idx].length - 1).each do |i|
366 cell = grid.dead_ends[current_idx][i]
367 already_placed_here = placed_foods.select {|food_loc| food_loc[:r] == cell.row && food_loc[:c] == cell.column}
368 if already_placed_here.length == 0
369 found = true
370 break
371 end
372 end
373 can_place = false if found
374 end
375 end
376
377 if can_place
378 food_js_arr << "{r:#{cell.row},c:#{cell.column},t:#{current_food_count}}" if (cell.row > 0 && cell.column > 0)
379 placed_foods << {:r => cell.row, :c => cell.column}
380 else
381 break
382 end
383
384 current_idx = idxs[idxsc]
385
386 idxsc += 1
387 if idxsc == idxs.length
388 idxsc = 0
389 end
390 current_food_count += 1
391 if current_food_count == max_food_count
392 current_food_count = 0
393 end
394 end
395 food_js << food_js_arr.join(',') << "];\n"
396 return food_js, placed_foods
397 end
398
399 def place_torches(grid)
400 torches = grid.identify_torch_locations
401 torch_data = []
402 torches.each do |torch|
403 torch_data << "{i:#{torch[:i]},j:#{torch[:j]},p:#{torch[:number]}}"
404 end
405 torch_str = "var torch_placements=[#{torch_data.join(',')}];\n"
406 storch_str = Utils.split(torch_str, 80)
407 storch_str
408 end
409
410 def place_doors(grid, exit_row, exit_col)
411 doors_js = []
412 # Create a few rooms in the grid
413 room_count = 0 #rand(4 .. 10)
414 (1..room_count).each do
415 room_js = create_room(grid)
416 if room_js
417 doors_js << room_js
418 else
419 break
420 end
421 end
422 # 0 -> cell with a top door, 1 -> cell with a bottom door in
423 # adding a door at (0,0) just for visual effects :)
424 # add another door at (width, height) to indicate exit
425 doors_js << '{r:0,c:0,v:0}'
426 doors_js << "{r:#{exit_row},c:#{exit_col},v:5}"
427 doors_str = "var doors=[ #{doors_js.join(',')}];\nvar exit_row=#{exit_row};\nvar exit_col=#{exit_col};";
428 sdoors_str = Utils.split(doors_str, 80)
429 sdoors_str
430 end
431
432 def place_weapons(food_locations, grid, height, width)
433 weapons_js = 'var weapon_locations = ['
434 weapon_locs = []
435 all_weapons = [0, 1, 2, 3] # 0 - spear, armor, shoe, ring
436 special_weapons = [2, 3].to_set
437 shoe_distance = 0
438 ring_distance = 0
439
440 all_weapons.each do |weapon|
441 distance_len = 101
442 begin
443 wcol = 1 + rand(width - 2)
444 wrow = 1+ rand(height - 2)
445
446 fl = 0
447 fp = ''
448 wep_cell = grid[wrow, wcol]
449 cell00 = grid[0, 0]
450 distance_len, _ = distance_calculator(wep_cell, grid, cell00, fl, fp)
451
452 if weapon == 2
453 shoe_distance = distance_len
454 end
455
456 if weapon == 3
457 ring_distance = distance_len
458 end
459
460 foods_already_placed_here = food_locations.select {|food_loc| food_loc[:r] == wrow && food_loc[:c] == wcol}
461 food_here = foods_already_placed_here.length > 0
462
463 weapons_placed_here = weapon_locs.select {|weapon_loc| weapon_loc[:r] == wrow && weapon_loc[:c] == wcol}
464 weapon_here = weapons_placed_here.length > 0
465
466 end while (distance_len > 101 && special_weapons.include?(weapon)) || food_here || weapon_here
467
468 weapon_locs << {:r => wrow, :c => wcol, :t => weapon}
469 end
470 weap_tmp_arr=[]
471 weapon_locs.each {|wpl| weap_tmp_arr << "{r:#{wpl[:r]},c:#{wpl[:c]},t:#{wpl[:t]}}"}
472 weapons_js << weap_tmp_arr.join(',') << "];\n"
473 return weapons_js, shoe_distance, ring_distance
474 end
475
476 #
477 # Will create a new maze with the given number as the seed for the random generator.
478 #
479 def handle_maze_with_number(level_number, game_id, main_html='lab.html', game_type = GAME_TYPE_STORY)
480
481 current_seed = 0x2ECD06801D ^ level_number
482 # Set the seed based on the level number
483 srand(current_seed)
484
485 minwidth = 10
486 minheight = 10
487
488 maxwidth = 30
489 maxheight = 30
490
491 # There are no walkers, so the system should be able to handle it
492 if game_type == GAME_TYPE_MAZE
493 maxwidth = 40
494 maxheight = 40
495 end
496
497 maxwidth = [minwidth + level_number, maxwidth].min
498 maxheight = [minheight + level_number, maxheight].min
499
500 width = rand(minwidth .. maxwidth)
501 height = rand(minheight ..maxheight)
502
503 $LOG.info "GameID:#{game_id} Generated: #{width} x #{height} for level #{level_number}"
504
505 grid = DistanceGrid.new(height, width)
506 wilson = Wilsons.new
507 wilson.on grid
508
509 maze_js = ''
510 maze_js << "history.pushState(null, null, '/#{game_id}');"
511 maze_js << "var w = #{width};\n"
512 maze_js << "var h = #{height};\n"
513 maze_js << "#{grid.maze_js_array}\n"
514
515 # Now identify all the places where a torch can go
516 storch_str = place_torches(grid)
517 maze_js << storch_str
518
519 # Where the exit is for now
520 exit_col = width - 1
521 exit_row = height- 1
522
523
524 # At a later stage the exit will not be exactly at the end of the maze, but at the furthest cell from (0,0)
525 # start = grid[0,0]
526 # distances = start.distances
527 # new_start, _ = distances.max
528 #if game_type == GAME_TYPE_ADVENTURE
529 # exit_col = new_start.column
530 # exit_row = new_start.row
531 #end
532
533 # Calculate the shortest path from the entrance to the exit
534 fp_fl = 0
535 fp_fp = ''
536 fp_wep_cell = grid[exit_row, exit_col]
537 fp_cell00 = grid[0,0]
538 full_path_len, _ = distance_calculator(fp_wep_cell, grid, fp_cell00, fp_fl, fp_fp)
539 $LOG.info "Distance from (0,0) to (#{exit_col}, #{exit_row}) is #{full_path_len} steps"
540
541 loot_value = 0
542 walkers = []
543 food_locations = []
544
545 # Create a few rooms with doors. Notes:
546 # 1. This is not done due to various reasons yet
547
548 sdoors_str = place_doors(grid, exit_row, exit_col)
549 maze_js << sdoors_str
550
551 # Create a few walkers but only if the game type is not a simple maze runner
552 if game_type != GAME_TYPE_MAZE
553 max_walkers = 0
554 if game_type == GAME_TYPE_STORY
555 max_walkers = [ [1, level_number - 3].max, 15].min if level_number > 4
556 else
557 max_walkers = rand(3..15)
558 end
559
560 walkers = generate_walkers(grid, max_walkers)
561 walkers_str_array = []
562 walkers.each do |walker|
563 walkers_str_array << "{t:#{walker[:body]},#{walker[:path]}}"
564 end
565 walkers_js = "var walkers=[#{walkers_str_array.join(',')}];\n"
566 maze_js << Utils.split(walkers_js, 80)
567 maze_js << "var walkers_count = #{walkers.length};"
568 else
569 maze_js << "var walkers=[];\nvar walkers_count=0;"
570 end
571
572 # Food items
573 if game_type != GAME_TYPE_MAZE
574 food_js, food_locations = place_food(grid, level_number, game_type)
575 maze_js << "#{food_js}"
576 else
577 maze_js << "var food=[];\n"
578 end
579
580 # Loot in the maze only if this is an adventure game
581 if game_type == GAME_TYPE_ADVENTURE
582 loot_js, loot_locations, loot_value = place_loot(grid, food_locations)
583
584 # Just hack the food locations to contain all the loot
585 (food_locations << loot_locations).flatten!
586 else
587 loot_js = "var loot=[];\n"
588 end
589
590 maze_js << "#{loot_js}"
591
592 shoe_distance = 0
593 ring_distance = 0
594
595 # Weapons
596 if game_type != GAME_TYPE_MAZE
597 weapons_js,shoe_distance,ring_distance = place_weapons(food_locations, grid, height, width)
598 maze_js << "#{weapons_js}"
599 else
600 maze_js << "var weapon_locations=[];\n"
601 end
602
603 templated_maze = IO.binread(main_html)
604 if $arrives.has_key? level_number
605 templates_arrives = $arrives[level_number]
606 else
607 templates_arrives = $generic_arrives
608 end
609 complete_templates templates_arrives
610
611 templates_arrives[:gid] = "#{game_id}"
612 templates_arrives[:lid] = "#{level_number}"
613 templates_arrives[:page_loader] = 'upon_page_load();'
614 # templates_arrives[:page_loader] = 'setup_labyrinth();' if game_type == GAME_TYPE_ADVENTURE or game_type == GAME_TYPE_MAZE
615
616 templates_arrives[:sysmenu_visible] = 'hidden;'
617 templates_arrives[:sysmenu_display] = 'none;'
618 templates_arrives[:menu_line_visible] = 'visible;'
619
620 level_statistics = "Level Number:#{level_number}\nExit at (#{exit_col}, #{exit_row}).\nDistance from (0,0) to (#{exit_col}, #{exit_row}) is #{full_path_len}"
621
622 templates_arrives[:badbrowser_visible] = 'hidden;'
623 templates_arrives[:badbrowser_display] = 'none;'
624 templates_arrives[:health_visible] = 'visible'
625 templates_arrives[:statis_visible] = 'hidden'
626 templates_arrives[:gameid_size] = '105px'
627 templates_arrives[:gameid_visible] = 'visible'
628 templates_arrives[:equipment_visible] = 'hidden'
629 templates_arrives[:messages_visible] = 'hidden; display:none'
630
631 if game_type == GAME_TYPE_ADVENTURE
632 templates_arrives[:gold_visible] = 'visible'
633 templates_arrives[:skeletons_visible] = 'visible'
634 templates_arrives[:levelctr_visible] = 'hidden; display:none'
635 templates_arrives[:timer_visible] = 'visible'
636 templates_arrives[:day_or_level] = 'Level'
637 templates_arrives[:new_game_type] = 'new_adventure_game'
638 level_statistics << "\nGold to be found in this dungeon: #{loot_value}\nSkeletons: #{walkers.length}\nSkeletons' acumen: Droidlike\n"
639 level_statistics << "Boots of Lightness at #{shoe_distance} steps apart from the start\nRing of Health #{ring_distance} steps apart from the start"
640
641 else
642 templates_arrives[:gold_visible] = 'hidden; display:none'
643 templates_arrives[:skeletons_visible] = 'hidden; display:none'
644 templates_arrives[:levelctr_visible] = 'visible'
645 templates_arrives[:timer_visible] = 'hidden; display:none'
646 templates_arrives[:new_game_type] = 'new_story_game'
647 templates_arrives[:day_or_level] = 'Day'
648
649 if game_type == GAME_TYPE_MAZE
650 templates_arrives[:levelctr_visible] = 'hidden; display:none'
651 templates_arrives[:health_visible] = 'hidden; display:none'
652 templates_arrives[:equipment_visible] = 'hidden; display:none'
653 templates_arrives[:timer_visible] = 'visible'
654 templates_arrives[:statis_visible] = 'visible'
655 templates_arrives[:new_game_type] = 'new_timechaser_game'
656 else
657 templates_arrives[:messages_visible] = 'visible'
658 end
659
660 end
661
662 templates_arrives[:story_game_loc] = Game.gen_game_url 'SG'
663 templates_arrives[:adv_game_loc] = Game.gen_game_url 'AG'
664 templates_arrives[:timer_game_loc] = Game.gen_game_url 'TG'
665
666 templates_arrives[:level_statistics] = level_statistics
667
668 full_maze = SimpleHtmlRenderer.render(templated_maze, templates_arrives)
669 full_maze << '<script type="text/javascript">'
670
671
672 gt = case game_type
673 when GAME_TYPE_ADVENTURE then 'GAME_TYPE_ADVENTURE'
674 when GAME_TYPE_STORY then 'GAME_TYPE_STORY'
675 when GAME_TYPE_MAZE then 'GAME_TYPE_TIMERUN'
676 else 'GAME_TYPE_TIMERUN'
677 end
678 javascripts = ''
679
680 javascripts << "const GAME_TYPE_ADVENTURE = 1;\nconst GAME_TYPE_STORY = 2;\nconst GAME_TYPE_TIMERUN = 3\n;var game_type=#{gt};\n"
681
682
683 javascripts << maze_js
684
685 if game_type == GAME_TYPE_ADVENTURE
686 templates_descends = $adventure_descend_summary
687 else
688 if game_type == GAME_TYPE_STORY
689 if $descends.has_key? level_number
690 templates_descends = $descends[level_number]
691 else
692 templates_descends = $generic_descends
693 end
694 else
695 templates_descends = $timerun_descends
696 end
697 end
698
699 javascripts << "var story_text =\"#{templates_descends[:story_text]}\";\n"
700 javascripts << "var leave_text =\"#{templates_descends[:leave_text]}\";\n"
701
702
703 javascripts << '</script>'
704
705 # javascripts << '<script type="text/javascript" src="graphics.js"></script>'
706 #
707 # javascripts << '<script type="text/javascript" src="game.js"></script>'
708
709 # templated_game_js = IO.binread 'game.js'
710 # game_js = SimpleHtmlRenderer.render(templated_game_js, templates_descends)
711
712 # javascripts << game_js
713
714 full_maze << javascripts
715
716 full_maze << 'request("graphics.js");</script></body></html>'
717 retv = [full_maze]
718
719 return 200, retv
720 end
721
722 #
723 # Handles the root request. Creates a new maze number
724 #
725 def handle_root
726 templated_maze = IO.binread(MAIN_HTML)
727 templates_arrives = $generic_arrives
728 complete_templates templates_arrives
729
730 templates_arrives[:gid] = ''
731 templates_arrives[:lid] = ''
732
733 templates_arrives[:page_loader] = 'page_loader();'
734
735 templates_arrives[:sysmenu_visible] = 'visible;'
736 templates_arrives[:sysmenu_display] = 'block;'
737 templates_arrives[:menu_line_visible] = 'hidden;'
738
739 templates_arrives[:gameid_visible] = 'hidden'
740 templates_arrives[:gold_visible] = 'hidden'
741 templates_arrives[:skeletons_visible] = 'hidden'
742 templates_arrives[:levelctr_visible] = 'hidden'
743 templates_arrives[:health_visible] = 'hidden'
744 templates_arrives[:timer_visible] = 'hidden'
745 templates_arrives[:gameid_size] = '0px; padding:0'
746 templates_arrives[:day_or_level] = 'Stage'
747 templates_arrives[:level_statistics] = 'Nothing here yet'
748 templates_arrives[:statis_visible] = 'hidden'
749 templates_arrives[:equipment_visible] = 'hidden'
750 templates_arrives[:new_game_type] = 'page_loader'
751 templates_arrives[:messages_visible] = 'hidden'
752
753 templates_arrives[:badbrowser_visible] = 'hidden;'
754 templates_arrives[:badbrowser_display] = 'none;'
755
756 templates_arrives[:story_game_loc] = Game.gen_game_url 'SG'
757 templates_arrives[:adv_game_loc] = Game.gen_game_url 'AG'
758 templates_arrives[:timer_game_loc] = Game.gen_game_url 'TG'
759
760 full_maze = SimpleHtmlRenderer.render(templated_maze, templates_arrives)
761 full_maze << '</body></html>'
762
763 retv = [full_maze]
764
765 return 200, retv
766 end
767
768 #
769 # Delivers a file to the browser
770 #
771 def deliver_file(name)
772
773 code, content_type, retv = page_not_found
774
775 # Will read a file. Supposed to be on the same level as the script
776 filename = name[1..-1]
777 if File.exists?(filename)
778
779 extension = filename.partition('.').last
780
781 code = 200
782 content_type = { CONTENT_TYPE => CONTENT_TYPES[extension] }
783 retv = [ IO.binread(name.partition('/').last) ]
784
785 end
786
787 return code, content_type, retv
788
789 end
790
791 def handle_game_loader(loc)
792
793 begin
794 key = loc[0..5]
795 enced = loc[6..-1]
796 deced = enced.decrypt key
797 rescue
798 deced = 'SG'
799 end
800 game_type = case deced
801 when 'SG' then
802 deced
803 when 'AG' then
804 deced
805 when 'TG' then
806 deced
807 else
808 'SG'
809 end
810 code, retv = Game.create_game game_type
811 return code, retv
812 end
813
814 def retrieve_game_id(request)
815 gid = ''
816 gid = request[:headers]['X-Extra'] if request[:headers].has_key? 'X-Extra'
817 gid = gid.split('=')[1] if gid && gid.count('=') > 0
818 gid
819 end
820
821 def handle_next_level(gid)
822
823 # See where the player was
824 level_nr = Database.get_db_level_for_gid gid
825
826 # Move him to the next level
827 level_nr += 1
828 Database.update_level_number_for_gid(level_nr, gid)
829
830 # And let's play that level
831 code, retv = handle_maze_with_number(level_nr, gid, MAIN_HTML, GAME_TYPE_STORY)
832
833 return code, retv
834 end
835
836 #
837 # Each connection will get in here
838 #
839 Net::HTTP::Server.run(:port => 8180) do |request, _|
840 begin
841 url = request[:uri]
842
843 code, content_type, retv = page_not_found
844
845 lab_id = url[:path]
846
847 # pp lab_id
848
849 # what we are trying to load
850 what = lab_id.to_s
851
852 # Handle favico
853 if what == '/favicon.ico'
854 code = 200
855 content_type = {CONTENT_TYPE => 'image/x-icon'}
856 retv = [ IO.binread('favicon.ico') ]
857 end
858
859 # Nothing, just load the default, and create a new maze
860 if what == '/'
861 code, retv = handle_root
862 elsif what != '/favicon.ico'
863 # some file that needs to be delivered?
864 if what.end_with?('.js') or what.end_with?('.png') or what.end_with?('.gif')
865 code, content_type, retv = deliver_file(what)
866 else
867 # The actual location
868 loc = what[1..-1]
869
870 # See if this is a gametype loader from the main screen, ie full hex string:
871 if loc =~ /^[0-9A-F]+$/i and not loc.is_i?
872 code, retv = handle_game_loader(loc)
873 else
874 # gid will be the Game ID of the current game if story mode, otherwise the adventure mode is selected for the
875 # current maze
876 gid = retrieve_game_id(request)
877
878 # Crazy locations will lead to random maze games
879 if loc.length > 9
880 loc = 'T' + Utils.random_string(8)
881 end
882 if gid == loc
883 # The player advanced to the next level from the game, we have a valid gid equaling the location of the window
884 code, retv = handle_next_level(gid)
885 elsif loc.is_i?
886 # We get here by a simple "GET" from the browser URL, someone just wants to play a maze game
887 # Now update in the DB the level number for gid
888 code, retv = handle_maze_with_number(loc.to_i, gid, MAIN_HTML, GAME_TYPE_MAZE)
889 elsif loc.length > 0 and (loc[0] == 'S' or loc[0] == 'A' or loc[0] == 'T')
890 level_number = Database.get_db_level_for_gid(loc)
891 if level_number == -1
892 level_number = Zlib::crc32(loc, 0)
893 end
894 code, retv = handle_maze_with_number(level_number, loc,
895 MAIN_HTML,
896 if loc[0] == 'S' then GAME_TYPE_STORY elsif loc[0] == 'T' then GAME_TYPE_MAZE
897 else GAME_TYPE_ADVENTURE end
898 )
899
900 end
901 end
902 end
903 end
904 rescue => ex
905 $LOG.fatal("A Somewhat critical error: #{ex.to_s}")
906 code, content_type, retv = internal_server_error
907 raise
908 end
909
910 # This will go back to the caller
911 reply = [code, content_type, retv]
912 reply
913
914 end
915