Mercurial > maze-src
diff server.rb @ 0:1eef88068f9f tip
initial commit of maze game source
| author | ferencd |
|---|---|
| date | Sun, 15 Sep 2019 11:46:47 +0200 |
| parents | |
| children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/server.rb Sun Sep 15 11:46:47 2019 +0200 @@ -0,0 +1,915 @@ +require 'net/http/server' +require 'pp' +require 'ellers' +require 'wilsons' +require 'distance_grid' +require 'pg' +require 'base62-rb' +require 'zlib' +require 'wlang' +require 'set' +require 'descends' +require 'pg' +require 'database' +require 'openssl' +require 'game' +require 'gs_logger' +require 'gamedata' + +# +# Starting the App, updating things that need to be +# +$LOG.info 'gameserver 0.1 starting' +Database.initialize + +# +# Constants +# +CONTENT_TYPE = 'Content-Type' +MAIN_HTML = 'lab.html' +CONTENT_TYPES = + { + :png => 'image/png', + :gif => 'image/gif', + :js => 'application/javascript' + } + +GAME_TYPE_MAZE = 1 +GAME_TYPE_ADVENTURE = 2 +GAME_TYPE_STORY = 3 + +################################################################################################################# +# Helper classes +################################################################################################################# + +# +# Used to get some extra numbers +# +class Integer + N_BYTES = [42].pack('i').size + N_BITS = N_BYTES * 16 + MAX = 2 ** (N_BITS - 2) - 1 + MIN = -MAX - 1 +end + +# +# An extension to the plain string class to provide b62 validations +# +class String + def valid_b62? + !!match(/^[[:alnum:]]+$/) + end + + def is_i? + /\A[-+]?\d+\z/ === self + end + + def encrypt(key) + cipher = OpenSSL::Cipher::Cipher.new('DES-EDE3-CBC').encrypt + cipher.key = Digest::SHA1.hexdigest key + s = cipher.update(self) + cipher.final + s.unpack('H*')[0].upcase + end + + def decrypt(key) + cipher = OpenSSL::Cipher::Cipher.new('DES-EDE3-CBC').decrypt + cipher.key = Digest::SHA1.hexdigest key + s = [self].pack('H*').unpack('C*').pack('c*') + cipher.update(s) + cipher.final + end + +end + +# +# Simple class to NOT to escape HTML code in the wlang renderer +# +class SimpleHtmlRenderer < WLang::Dialect + + def highlight(buf, fn) + var_name = render(fn) + var_value = evaluate(var_name) + buf << var_value + end + + tag '$', :highlight +end + + +################################################################################################################# +# Helper methods # +################################################################################################################# + +# +# Provides a fully completed template for the template engine +# +def complete_templates(templates) + (1..5).each do | v | + templates["gotext#{v}".to_sym] = '' unless templates.has_key? "gotext#{v}".to_sym + end + templates +end + +# +# Just returns page not found +# +def page_not_found + content_type = {CONTENT_TYPE => 'text/html'} + retv = [ '<html><center><strong>404 :-(</strong></center></html>' ] + code = 404 + return code, content_type, retv +end + +def internal_server_error + content_type = {CONTENT_TYPE => 'text/html'} + retv = [ '<html><center><strong>500 :-(</strong></center></html>' ] + code = 500 + return code, content_type, retv +end + + +# +# Will create a random room in the grid +# +def create_room(grid) + + tries = 0 + can_not_make = false + begin + + max_width = [grid.columns/10, 6].max + max_height = [grid.rows/10, 6].max + + grid_width = rand(2 .. max_width) + 2 + grid_height = rand(2 .. max_height) + 2 + + x1 = rand(grid.columns - grid_width - 4) + x2 = x1 + grid_width + y1 = rand(grid.rows - grid_height - 4) + y2 = y1 + grid_height + + tries += 1 + can_not_make = !grid.can_make_room(x1,y1,x2,y2) + + end while can_not_make && tries > 5 + + return '' if tries >= 5 + + grid.create_room x1, y1, x2, y2 + + # Place the door + door_x_cell = x1 + grid_width / 2 + door_y_top_cell = y1 + door_y_bottom_cell = y1 + 1 + + "{r:#{door_y_bottom_cell},c:#{door_x_cell},v:0}, {r:#{door_y_top_cell},c:#{door_x_cell},v:1}" +end + +# +# Calculates the distance from the goal cell on the given grid towards the cell_to_go +# and returns the final_len and final_path +# +def distance_calculator(goal, grid, cell_to_go, final_len, final_path) + another_distances = goal.distances + grid.distances = another_distances.path_to(cell_to_go) + jsv2 = grid.distances_js_array_path + final_len += jsv2.length + final_path += jsv2 + return final_len, final_path +end + +# +# Generates a path for the given walker +# +def create_walker_path(grid, r, c) + # making a new path from the furthest point from there + new_distances = grid[r, c].distances + + # goal will be the cell which is the furthest from the new_start and max_dist the length of it + goal, _ = new_distances.max + grid.distances = new_distances.path_to(goal) + + jsv = grid.distances_js_array_path + final_path = jsv + final_len = final_path.length + + # And the goal is that the walker will take a round around the maze + + # Now add in another path to (0,0) + final_len, final_path = distance_calculator(goal, grid, grid[0, 0],final_len, final_path) + final_len, final_path = distance_calculator(grid[0, 0], grid, grid[0, grid.columns - 1],final_len, final_path) + final_len, final_path = distance_calculator(grid[0, grid.columns - 1], grid, grid[grid.rows - 1, grid.columns - 1],final_len, final_path) + + s = '' + s << "path_len:#{final_len},r:#{r},c:#{c},p:'#{final_path}'" + s + +end + +# will generate a few walking characters +def generate_walkers(grid, max_nr) + walkers = [] + return walkers if max_nr == 0 + + walker_count = rand(1 .. max_nr + 1) + $LOG.info"Generating #{walker_count} walkers\n" + #walkers << {:body => 0, :row => 0, :col => 1, :path => "path_len:3,r:0,c:1,p:'EEE'"} + #walkers << {:body => 0, :row => 0, :col => 4, :path => "path_len:3,r:0,c:4,p:'WWW'"} + dbg_cwa = 0 + (1..walker_count).each do + walker_body = dbg_cwa #rand(3) + dbg_cwa = 1 if dbg_cwa == 0 + walker_row = rand(1 .. grid.rows - 2) + 1 + walker_col = rand(1 .. grid.columns - 2) + 1 + walkers << + { + :body => walker_body, + :row => walker_row, + :col => walker_col, + :path => create_walker_path(grid,walker_row,walker_col) + } + end + walkers +end + + +# +# Will put the loot stuff on the level. food_locations is an array where wealready have placed food +# +def place_loot(grid, food_locations) + loot_js = 'var loot=[' + max_possible_loot = grid.dead_ends[1].length + grid.dead_ends[2].length + grid.dead_ends[4].length + grid.dead_ends[8].length + max_possible_loot = max_possible_loot / 4 + 1 + + max_loot_count = $ITEMS.length # 32 items defined in the png_to_js + + loot_count = [rand(max_possible_loot), max_loot_count].min # We want a lot of loot in the maze + + current_loot_count = 0 + idxs = [1, 2, 4, 8] + idxsc = 0 + current_idx = idxs[idxsc] + loot_js_arr = [] + placed_loots = [] + current_loot_value = 0 + + (1..loot_count).each do |_| + + place_ctr = 0 + can_place = true + begin + cell = grid.dead_ends[current_idx][rand (grid.dead_ends[current_idx].length)] + place_ctr += 1 + already_placed_here = placed_loots.select {|loot_loc| loot_loc[:r] == cell.row && loot_loc[:c] == cell.column} + here_is_not_fine = already_placed_here.length > 0 + + unless here_is_not_fine + food_placed_here = food_locations.select {|food_loc| food_loc[:r] == cell.row && food_loc[:c] == cell.column} + here_is_not_fine |= food_placed_here.length > 0 + end + + end while here_is_not_fine && place_ctr < grid.dead_ends[current_idx].length-1 + + if place_ctr == grid.dead_ends[current_idx].length-1 + if already_placed_here.length > 0 + # try to find the first non empty place in the grid + found = false + (0..grid.dead_ends[current_idx].length - 1).each do |i| + cell = grid.dead_ends[current_idx][i] + + already_placed_here = placed_loots.select {|loot_loc| loot_loc[:r] == cell.row && loot_loc[:c] == cell.column} + can_go_here = already_placed_here.length == 0 + food_placed_here = food_locations.select {|loot_loc| loot_loc[:r] == cell.row && loot_loc[:c] == cell.column} + can_go_here &= food_placed_here.length == 0 + + if can_go_here + found = true + break + end + + end + can_place = false if found + end + end + + if can_place + loot_js_arr << "{r:#{cell.row},c:#{cell.column},t:#{current_loot_count}}" if (cell.row > 0 && cell.column > 0) + + current_loot_value += $ITEMS[current_loot_count][:value] + placed_loots << {:r => cell.row, :c => cell.column} + else + break + end + + current_idx = idxs[idxsc] + + idxsc += 1 + if idxsc == idxs.length + idxsc = 0 + end + current_loot_count += 1 + if current_loot_count == max_loot_count + current_loot_count = 0 + end + end + loot_js << loot_js_arr.join(',') << "];\n" + return loot_js, placed_loots, current_loot_value +end + +# +# Will put the food on the table ... ie. level +# +def place_food(grid, level_number, game_type) + food_js = 'var food=[' + max_possible_food = grid.dead_ends[1].length + grid.dead_ends[2].length + grid.dead_ends[4].length + grid.dead_ends[8].length + max_possible_food = max_possible_food / 4 + 1 + food_count = rand(max_possible_food) + + if game_type == GAME_TYPE_STORY + if level_number > 55 + food_count *= 4 + elsif level_number > 45 + food_count *= 3 + elsif level_number > 20 + food_count *= 2 + elsif level_number < 10 + food_count /= 2 + food_count += 1 + end + end + + max_food_count = 10 # This comes from png_to_js the number of possible food items available + current_food_count = 0 # Counts the food items of the max available + idxs = [1, 2, 4, 8] # The indexes of the cells, which are "dead ends". + idxsc = 0 # Counter for the "dead end" index array + current_idx = idxs[idxsc] # The current "dead end" index + food_js_arr = [] # Will hold the javascript array for the food stuff + placed_foods = [] # Will hold where the food was placed + + (1..food_count).each do |_| + + place_ctr = 0 + can_place = true + + # Find a place (cell) where we can place the food item + begin + cell = grid.dead_ends[current_idx][rand (grid.dead_ends[current_idx].length)] + place_ctr += 1 + already_placed_here = placed_foods.select {|food_loc| food_loc[:r] == cell.row && food_loc[:c] == cell.column} + end while already_placed_here.length>0 && place_ctr < grid.dead_ends[current_idx].length-1 + + if place_ctr == grid.dead_ends[current_idx].length-1 + # Did not find an "empty dead end" cell to put the food there + if already_placed_here.length > 0 + # try to find the first empty cell in the grid + found = false + (0..grid.dead_ends[current_idx].length - 1).each do |i| + cell = grid.dead_ends[current_idx][i] + already_placed_here = placed_foods.select {|food_loc| food_loc[:r] == cell.row && food_loc[:c] == cell.column} + if already_placed_here.length == 0 + found = true + break + end + end + can_place = false if found + end + end + + if can_place + food_js_arr << "{r:#{cell.row},c:#{cell.column},t:#{current_food_count}}" if (cell.row > 0 && cell.column > 0) + placed_foods << {:r => cell.row, :c => cell.column} + else + break + end + + current_idx = idxs[idxsc] + + idxsc += 1 + if idxsc == idxs.length + idxsc = 0 + end + current_food_count += 1 + if current_food_count == max_food_count + current_food_count = 0 + end + end + food_js << food_js_arr.join(',') << "];\n" + return food_js, placed_foods +end + +def place_torches(grid) + torches = grid.identify_torch_locations + torch_data = [] + torches.each do |torch| + torch_data << "{i:#{torch[:i]},j:#{torch[:j]},p:#{torch[:number]}}" + end + torch_str = "var torch_placements=[#{torch_data.join(',')}];\n" + storch_str = Utils.split(torch_str, 80) + storch_str +end + +def place_doors(grid, exit_row, exit_col) + doors_js = [] + # Create a few rooms in the grid + room_count = 0 #rand(4 .. 10) + (1..room_count).each do + room_js = create_room(grid) + if room_js + doors_js << room_js + else + break + end + end + # 0 -> cell with a top door, 1 -> cell with a bottom door in + # adding a door at (0,0) just for visual effects :) + # add another door at (width, height) to indicate exit + doors_js << '{r:0,c:0,v:0}' + doors_js << "{r:#{exit_row},c:#{exit_col},v:5}" + doors_str = "var doors=[ #{doors_js.join(',')}];\nvar exit_row=#{exit_row};\nvar exit_col=#{exit_col};"; + sdoors_str = Utils.split(doors_str, 80) + sdoors_str +end + +def place_weapons(food_locations, grid, height, width) + weapons_js = 'var weapon_locations = [' + weapon_locs = [] + all_weapons = [0, 1, 2, 3] # 0 - spear, armor, shoe, ring + special_weapons = [2, 3].to_set + shoe_distance = 0 + ring_distance = 0 + + all_weapons.each do |weapon| + distance_len = 101 + begin + wcol = 1 + rand(width - 2) + wrow = 1+ rand(height - 2) + + fl = 0 + fp = '' + wep_cell = grid[wrow, wcol] + cell00 = grid[0, 0] + distance_len, _ = distance_calculator(wep_cell, grid, cell00, fl, fp) + + if weapon == 2 + shoe_distance = distance_len + end + + if weapon == 3 + ring_distance = distance_len + end + + foods_already_placed_here = food_locations.select {|food_loc| food_loc[:r] == wrow && food_loc[:c] == wcol} + food_here = foods_already_placed_here.length > 0 + + weapons_placed_here = weapon_locs.select {|weapon_loc| weapon_loc[:r] == wrow && weapon_loc[:c] == wcol} + weapon_here = weapons_placed_here.length > 0 + + end while (distance_len > 101 && special_weapons.include?(weapon)) || food_here || weapon_here + + weapon_locs << {:r => wrow, :c => wcol, :t => weapon} + end + weap_tmp_arr=[] + weapon_locs.each {|wpl| weap_tmp_arr << "{r:#{wpl[:r]},c:#{wpl[:c]},t:#{wpl[:t]}}"} + weapons_js << weap_tmp_arr.join(',') << "];\n" + return weapons_js, shoe_distance, ring_distance +end + +# +# Will create a new maze with the given number as the seed for the random generator. +# +def handle_maze_with_number(level_number, game_id, main_html='lab.html', game_type = GAME_TYPE_STORY) + + current_seed = 0x2ECD06801D ^ level_number + # Set the seed based on the level number + srand(current_seed) + + minwidth = 10 + minheight = 10 + + maxwidth = 30 + maxheight = 30 + + # There are no walkers, so the system should be able to handle it + if game_type == GAME_TYPE_MAZE + maxwidth = 40 + maxheight = 40 + end + + maxwidth = [minwidth + level_number, maxwidth].min + maxheight = [minheight + level_number, maxheight].min + + width = rand(minwidth .. maxwidth) + height = rand(minheight ..maxheight) + + $LOG.info "GameID:#{game_id} Generated: #{width} x #{height} for level #{level_number}" + + grid = DistanceGrid.new(height, width) + wilson = Wilsons.new + wilson.on grid + + maze_js = '' + maze_js << "history.pushState(null, null, '/#{game_id}');" + maze_js << "var w = #{width};\n" + maze_js << "var h = #{height};\n" + maze_js << "#{grid.maze_js_array}\n" + + # Now identify all the places where a torch can go + storch_str = place_torches(grid) + maze_js << storch_str + + # Where the exit is for now + exit_col = width - 1 + exit_row = height- 1 + + + # At a later stage the exit will not be exactly at the end of the maze, but at the furthest cell from (0,0) + # start = grid[0,0] + # distances = start.distances + # new_start, _ = distances.max + #if game_type == GAME_TYPE_ADVENTURE + # exit_col = new_start.column + # exit_row = new_start.row + #end + + # Calculate the shortest path from the entrance to the exit + fp_fl = 0 + fp_fp = '' + fp_wep_cell = grid[exit_row, exit_col] + fp_cell00 = grid[0,0] + full_path_len, _ = distance_calculator(fp_wep_cell, grid, fp_cell00, fp_fl, fp_fp) + $LOG.info "Distance from (0,0) to (#{exit_col}, #{exit_row}) is #{full_path_len} steps" + + loot_value = 0 + walkers = [] + food_locations = [] + + # Create a few rooms with doors. Notes: + # 1. This is not done due to various reasons yet + + sdoors_str = place_doors(grid, exit_row, exit_col) + maze_js << sdoors_str + + # Create a few walkers but only if the game type is not a simple maze runner + if game_type != GAME_TYPE_MAZE + max_walkers = 0 + if game_type == GAME_TYPE_STORY + max_walkers = [ [1, level_number - 3].max, 15].min if level_number > 4 + else + max_walkers = rand(3..15) + end + + walkers = generate_walkers(grid, max_walkers) + walkers_str_array = [] + walkers.each do |walker| + walkers_str_array << "{t:#{walker[:body]},#{walker[:path]}}" + end + walkers_js = "var walkers=[#{walkers_str_array.join(',')}];\n" + maze_js << Utils.split(walkers_js, 80) + maze_js << "var walkers_count = #{walkers.length};" + else + maze_js << "var walkers=[];\nvar walkers_count=0;" + end + + # Food items + if game_type != GAME_TYPE_MAZE + food_js, food_locations = place_food(grid, level_number, game_type) + maze_js << "#{food_js}" + else + maze_js << "var food=[];\n" + end + + # Loot in the maze only if this is an adventure game + if game_type == GAME_TYPE_ADVENTURE + loot_js, loot_locations, loot_value = place_loot(grid, food_locations) + + # Just hack the food locations to contain all the loot + (food_locations << loot_locations).flatten! + else + loot_js = "var loot=[];\n" + end + + maze_js << "#{loot_js}" + + shoe_distance = 0 + ring_distance = 0 + + # Weapons + if game_type != GAME_TYPE_MAZE + weapons_js,shoe_distance,ring_distance = place_weapons(food_locations, grid, height, width) + maze_js << "#{weapons_js}" + else + maze_js << "var weapon_locations=[];\n" + end + + templated_maze = IO.binread(main_html) + if $arrives.has_key? level_number + templates_arrives = $arrives[level_number] + else + templates_arrives = $generic_arrives + end + complete_templates templates_arrives + + templates_arrives[:gid] = "#{game_id}" + templates_arrives[:lid] = "#{level_number}" + templates_arrives[:page_loader] = 'upon_page_load();' + # templates_arrives[:page_loader] = 'setup_labyrinth();' if game_type == GAME_TYPE_ADVENTURE or game_type == GAME_TYPE_MAZE + + templates_arrives[:sysmenu_visible] = 'hidden;' + templates_arrives[:sysmenu_display] = 'none;' + templates_arrives[:menu_line_visible] = 'visible;' + + 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}" + + templates_arrives[:badbrowser_visible] = 'hidden;' + templates_arrives[:badbrowser_display] = 'none;' + templates_arrives[:health_visible] = 'visible' + templates_arrives[:statis_visible] = 'hidden' + templates_arrives[:gameid_size] = '105px' + templates_arrives[:gameid_visible] = 'visible' + templates_arrives[:equipment_visible] = 'hidden' + templates_arrives[:messages_visible] = 'hidden; display:none' + + if game_type == GAME_TYPE_ADVENTURE + templates_arrives[:gold_visible] = 'visible' + templates_arrives[:skeletons_visible] = 'visible' + templates_arrives[:levelctr_visible] = 'hidden; display:none' + templates_arrives[:timer_visible] = 'visible' + templates_arrives[:day_or_level] = 'Level' + templates_arrives[:new_game_type] = 'new_adventure_game' + level_statistics << "\nGold to be found in this dungeon: #{loot_value}\nSkeletons: #{walkers.length}\nSkeletons' acumen: Droidlike\n" + level_statistics << "Boots of Lightness at #{shoe_distance} steps apart from the start\nRing of Health #{ring_distance} steps apart from the start" + + else + templates_arrives[:gold_visible] = 'hidden; display:none' + templates_arrives[:skeletons_visible] = 'hidden; display:none' + templates_arrives[:levelctr_visible] = 'visible' + templates_arrives[:timer_visible] = 'hidden; display:none' + templates_arrives[:new_game_type] = 'new_story_game' + templates_arrives[:day_or_level] = 'Day' + + if game_type == GAME_TYPE_MAZE + templates_arrives[:levelctr_visible] = 'hidden; display:none' + templates_arrives[:health_visible] = 'hidden; display:none' + templates_arrives[:equipment_visible] = 'hidden; display:none' + templates_arrives[:timer_visible] = 'visible' + templates_arrives[:statis_visible] = 'visible' + templates_arrives[:new_game_type] = 'new_timechaser_game' + else + templates_arrives[:messages_visible] = 'visible' + end + + end + + templates_arrives[:story_game_loc] = Game.gen_game_url 'SG' + templates_arrives[:adv_game_loc] = Game.gen_game_url 'AG' + templates_arrives[:timer_game_loc] = Game.gen_game_url 'TG' + + templates_arrives[:level_statistics] = level_statistics + + full_maze = SimpleHtmlRenderer.render(templated_maze, templates_arrives) + full_maze << '<script type="text/javascript">' + + + gt = case game_type + when GAME_TYPE_ADVENTURE then 'GAME_TYPE_ADVENTURE' + when GAME_TYPE_STORY then 'GAME_TYPE_STORY' + when GAME_TYPE_MAZE then 'GAME_TYPE_TIMERUN' + else 'GAME_TYPE_TIMERUN' + end + javascripts = '' + + javascripts << "const GAME_TYPE_ADVENTURE = 1;\nconst GAME_TYPE_STORY = 2;\nconst GAME_TYPE_TIMERUN = 3\n;var game_type=#{gt};\n" + + + javascripts << maze_js + + if game_type == GAME_TYPE_ADVENTURE + templates_descends = $adventure_descend_summary + else + if game_type == GAME_TYPE_STORY + if $descends.has_key? level_number + templates_descends = $descends[level_number] + else + templates_descends = $generic_descends + end + else + templates_descends = $timerun_descends + end + end + + javascripts << "var story_text =\"#{templates_descends[:story_text]}\";\n" + javascripts << "var leave_text =\"#{templates_descends[:leave_text]}\";\n" + + + javascripts << '</script>' + +# javascripts << '<script type="text/javascript" src="graphics.js"></script>' +# +# javascripts << '<script type="text/javascript" src="game.js"></script>' + +# templated_game_js = IO.binread 'game.js' +# game_js = SimpleHtmlRenderer.render(templated_game_js, templates_descends) + +# javascripts << game_js + + full_maze << javascripts + + full_maze << 'request("graphics.js");</script></body></html>' + retv = [full_maze] + + return 200, retv +end + +# +# Handles the root request. Creates a new maze number +# +def handle_root + templated_maze = IO.binread(MAIN_HTML) + templates_arrives = $generic_arrives + complete_templates templates_arrives + + templates_arrives[:gid] = '' + templates_arrives[:lid] = '' + + templates_arrives[:page_loader] = 'page_loader();' + + templates_arrives[:sysmenu_visible] = 'visible;' + templates_arrives[:sysmenu_display] = 'block;' + templates_arrives[:menu_line_visible] = 'hidden;' + + templates_arrives[:gameid_visible] = 'hidden' + templates_arrives[:gold_visible] = 'hidden' + templates_arrives[:skeletons_visible] = 'hidden' + templates_arrives[:levelctr_visible] = 'hidden' + templates_arrives[:health_visible] = 'hidden' + templates_arrives[:timer_visible] = 'hidden' + templates_arrives[:gameid_size] = '0px; padding:0' + templates_arrives[:day_or_level] = 'Stage' + templates_arrives[:level_statistics] = 'Nothing here yet' + templates_arrives[:statis_visible] = 'hidden' + templates_arrives[:equipment_visible] = 'hidden' + templates_arrives[:new_game_type] = 'page_loader' + templates_arrives[:messages_visible] = 'hidden' + + templates_arrives[:badbrowser_visible] = 'hidden;' + templates_arrives[:badbrowser_display] = 'none;' + + templates_arrives[:story_game_loc] = Game.gen_game_url 'SG' + templates_arrives[:adv_game_loc] = Game.gen_game_url 'AG' + templates_arrives[:timer_game_loc] = Game.gen_game_url 'TG' + + full_maze = SimpleHtmlRenderer.render(templated_maze, templates_arrives) + full_maze << '</body></html>' + + retv = [full_maze] + + return 200, retv +end + +# +# Delivers a file to the browser +# +def deliver_file(name) + + code, content_type, retv = page_not_found + + # Will read a file. Supposed to be on the same level as the script + filename = name[1..-1] + if File.exists?(filename) + + extension = filename.partition('.').last + + code = 200 + content_type = { CONTENT_TYPE => CONTENT_TYPES[extension] } + retv = [ IO.binread(name.partition('/').last) ] + + end + + return code, content_type, retv + +end + +def handle_game_loader(loc) + + begin + key = loc[0..5] + enced = loc[6..-1] + deced = enced.decrypt key + rescue + deced = 'SG' + end + game_type = case deced + when 'SG' then + deced + when 'AG' then + deced + when 'TG' then + deced + else + 'SG' + end + code, retv = Game.create_game game_type + return code, retv +end + +def retrieve_game_id(request) + gid = '' + gid = request[:headers]['X-Extra'] if request[:headers].has_key? 'X-Extra' + gid = gid.split('=')[1] if gid && gid.count('=') > 0 + gid +end + +def handle_next_level(gid) + + # See where the player was + level_nr = Database.get_db_level_for_gid gid + + # Move him to the next level + level_nr += 1 + Database.update_level_number_for_gid(level_nr, gid) + + # And let's play that level + code, retv = handle_maze_with_number(level_nr, gid, MAIN_HTML, GAME_TYPE_STORY) + + return code, retv +end + +# +# Each connection will get in here +# +Net::HTTP::Server.run(:port => 8180) do |request, _| + begin + url = request[:uri] + + code, content_type, retv = page_not_found + + lab_id = url[:path] + + # pp lab_id + + # what we are trying to load + what = lab_id.to_s + + # Handle favico + if what == '/favicon.ico' + code = 200 + content_type = {CONTENT_TYPE => 'image/x-icon'} + retv = [ IO.binread('favicon.ico') ] + end + + # Nothing, just load the default, and create a new maze + if what == '/' + code, retv = handle_root + elsif what != '/favicon.ico' + # some file that needs to be delivered? + if what.end_with?('.js') or what.end_with?('.png') or what.end_with?('.gif') + code, content_type, retv = deliver_file(what) + else + # The actual location + loc = what[1..-1] + + # See if this is a gametype loader from the main screen, ie full hex string: + if loc =~ /^[0-9A-F]+$/i and not loc.is_i? + code, retv = handle_game_loader(loc) + else + # gid will be the Game ID of the current game if story mode, otherwise the adventure mode is selected for the + # current maze + gid = retrieve_game_id(request) + + # Crazy locations will lead to random maze games + if loc.length > 9 + loc = 'T' + Utils.random_string(8) + end + if gid == loc + # The player advanced to the next level from the game, we have a valid gid equaling the location of the window + code, retv = handle_next_level(gid) + elsif loc.is_i? + # We get here by a simple "GET" from the browser URL, someone just wants to play a maze game + # Now update in the DB the level number for gid + code, retv = handle_maze_with_number(loc.to_i, gid, MAIN_HTML, GAME_TYPE_MAZE) + elsif loc.length > 0 and (loc[0] == 'S' or loc[0] == 'A' or loc[0] == 'T') + level_number = Database.get_db_level_for_gid(loc) + if level_number == -1 + level_number = Zlib::crc32(loc, 0) + end + code, retv = handle_maze_with_number(level_number, loc, + MAIN_HTML, + if loc[0] == 'S' then GAME_TYPE_STORY elsif loc[0] == 'T' then GAME_TYPE_MAZE + else GAME_TYPE_ADVENTURE end + ) + + end + end + end + end + rescue => ex + $LOG.fatal("A Somewhat critical error: #{ex.to_s}") + code, content_type, retv = internal_server_error + raise + end + + # This will go back to the caller + reply = [code, content_type, retv] + reply + +end +
