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
+