ソースを参照

first running version with server-admin-skills

Paul Klumpp 12 年 前
コミット
3ab3579a2a
1 ファイル変更450 行追加33 行削除
  1. 450 33
      em_server.rb

+ 450 - 33
em_server.rb

@@ -13,7 +13,7 @@ require 'timeout'
 # .. allowed commands .. ROLES = CAPABILITIES
 # normal users have ROLE broadcast. Roles are defined on a per-user basis .. in a config.
 
-$version = "0.4em_cooldns_classes_for_caching"
+$version = "0.6em_specservers_dupe_election"
 $debug = 0
 
 $role_commands = Hash[
@@ -23,8 +23,8 @@ $role_commands = Hash[
   'broadcast_admin' => %w(BC_ID BC BC_ENDCOUNT),
   'broadcast' => %w(REQ_BC BC_RE),
 	
-	'specbot_admin' => %w(REQ_ASSIGN REQ_UNASSIGN REQ_PING REQ_ASSIGNMENTS),
-	'specbot' => %w(ASSIGN_RE UNASSIGN_RE PING_RE ASSIGNMENTS_RE REQ_DNS),
+	'specbot_admin' => %w(REQ_ASSIGN REQ_UNASSIGN REQ_PING REQ_ASSIGNMENTS REQ_MAXSERVERS DUPECHECK ELECTION),
+	'specbot' => %w(ASSIGN_RE UNASSIGN_RE PING_RE ASSIGNMENTS_RE REQ_DNS MAXSERVERS_RE),
 ]
 
 $default_role = "everyone"
@@ -44,7 +44,7 @@ $role_dialogs = Hash[
 
 $user_roles = Hash[
   #noinspection RubyStringKeysInHashInspection
-  'paul_tester' => %w(everyone broadcast specbot specbot_admin),
+  'paul_tester' => %w(everyone broadcast specbot_admin),
 
 	'paul_dev_eggdrop' => %w(everyone broadcast),
 	'paul_eggdrop' => %w(everyone broadcast),
@@ -54,8 +54,9 @@ $user_roles = Hash[
 	
 	'qw.nu' => %w(everyone broadcast),
 	'qw.nu_poster' => %w(everyone broadcast),
-	
-	'mihawk_dev_specbot' => %w(everyone broadcast specbot),
+
+  'mihawk_devA_specbot' => %w(everyone broadcast specbot),
+  'mihawk_devB_specbot' => %w(everyone broadcast specbot),
 	'mihawk_specbot' => %w(everyone broadcast specbot),
 
   'armitage_specbot' => %w(everyone broadcast specbot),
@@ -351,6 +352,12 @@ $qw_list = [
         "210.50.4.11:27504",
         ]
 
+# for local dev usage ;)
+$qw_list = [
+    "89.104.194.146:27501",
+    "210.50.4.11:27504",
+    "122.99.118.2:28001",
+]
 
 class GameServers
 
@@ -443,7 +450,7 @@ class GameServers
       scanserver(gserver, type)
     end
     put_log "End of scanning."
-    put_log "foppa: #{@gameservers.fetch("89.104.194.146:27503")} "
+    #put_log "foppa: #{@gameservers.fetch("89.104.194.146:27501")} "
   end # of scanserverlist()
 
   def get_cool_dns(iphost, ipport)
@@ -659,19 +666,127 @@ module CentralProtocolHandler
   # default method that is being run on connection!
 	def post_init # connection of someone starts here...
 		@username = nil
-	end
+    @my_servers = Hash.new
+    @my_servers.default = {
+      "active" => 0,
+      "s-p" => "false",
+      "ping" => 8888,
+    }
+  end
 
+  ### setters
+  def set_my_servers(server, key, value)
+    current = Hash.new
+    current.store(key, value)
+    @my_servers[server] = @my_servers[server].merge(current)
+  end
+
+  def specbot_set_sp(username, server, value)
+    @@connected_clients.each {|c|
+      if c.username == username
+        c.set_my_servers(server, "s-p", value)
+      end
+    }
+  end
 
-	### getters
+  ### getters
 	def entered_username?
 		!@username.nil? && !@username.empty? # then it's true
 	end
 
 	def username
 		@username
-	end
+  end
+
+  def my_active_servers(active = true)
+    unless @my_servers.nil? || @my_servers.empty?
+
+      if active
+        rethash = Hash.new
+        @my_servers.each_pair{|k, v|
+          if v["active"] == 1
+            rethash[k] = @my_servers[k]
+          end
+        }
+        rethash
+      else
+        @my_servers
+      end
+    end
+  end
+
+  def specbot_servers(flat = true)
+    the_servers = Hash.new
+    @@connected_clients.each {|c|
+      if flat
+        unless c.my_active_servers.nil? || c.my_active_servers.empty?
+          the_servers = the_servers.merge(c.my_active_servers)
+        end
+
+      else
+        unless c.my_active_servers.nil? || c.my_active_servers.empty?
+          the_servers[c.username] = c.my_active_servers
+        end
+
+      end
+    }
+    the_servers
+  end
+
+  def specbot_ping(server)
+    ms = my_active_servers(false)
+    unless ms.nil?
+      si = ms[server]
+      p1 = si.fetch("ping").to_s.to_i
+
+      return p1
+    else
+      return 8888
+    end
+  end
+
+  def specbot_active?(server)
+    ms = my_active_servers(false)
+    unless ms.nil?
+      si = ms[server]
+      a1 = si.fetch("active")
+    else
+      a1 = 0
+    end
+
+    if a1 == 1
+      return 1
+    else
+      return 0
+    end
+  end
+
+  def specbot_sp?(server)
+    ms = my_active_servers(false)
+    unless ms.nil?
+      si = ms[server]
+      a1 = si.fetch("s-p")
+    else
+      a1 = "false"
+    end
 
-	def my_roles
+    if a1 == "true"
+      return true
+    else
+      return false
+    end
+  end
+
+  def my_maxservers
+    if @my_maxservers.nil? || @my_maxservers.empty?
+      nil
+    else
+      @my_maxservers
+    end
+  end
+
+
+  def my_roles
 		$user_roles[@username]
 	end
 
@@ -685,7 +800,29 @@ module CentralProtocolHandler
 		@@connected_clients.each {|c| users.push(c.username)}
 
 		users
-	end # of ousers
+  end # of ousers
+
+  # returns online users by searching through saved connections that have the specified role
+  def ousers_by_role(role)
+    users = Array.new
+    @@connected_clients.each {|c|
+      if c.my_roles.include?(role)
+        users.push(c.username)
+      end
+    }
+    users
+  end
+
+  # returns connections by searching through saved connections that have the specified role
+  def connections_by_role(role)
+    conns = Array.new
+    @@connected_clients.each {|c|
+      if c.my_roles.include?(role)
+        conns.push(c)
+      end
+    }
+    conns
+  end
 
 	### checkers
 	def allowed_cmd(inputmessage)
@@ -710,6 +847,58 @@ module CentralProtocolHandler
 
 
 	### actions
+  def specbot_dupecheck()
+    put_log "specbot_dupecheck drin"
+    conns = connections_by_role("specbot")
+
+    # if this connection has a same key than another connection
+    # then fight .. which connection should have that server unassigned. REQ_PING
+    # the one with the better ping will win and the one with the worse ping will get a new free server slot ;)
+
+    # we have at least 2 bots... > 1
+    conns.repeated_combination(2){|c|
+      c1 = c[0]
+      c2 = c[1]
+      unless c1 == c2
+        unless c1.my_active_servers.nil? || c2.my_active_servers.nil?
+
+          dupes = c1.my_active_servers.keys & c2.my_active_servers.keys
+
+          if dupes.size > 0
+            # damn, there's dupes.
+            put_log "the dupes are: #{dupes.join(", ")} on #{c1.username} and #{c2.username}"
+            dupes.each{|d|
+              write_user("REQ_PING #{d}", c1.username)
+              write_user("REQ_PING #{d}", c2.username)
+
+              EventMachine.add_timer 2, proc {
+
+                p1 = c1.specbot_ping(d)
+                p2 = c2.specbot_ping(d)
+
+                a1 = c1.specbot_active?(d)
+                a2 = c2.specbot_active?(d)
+
+                if p1 <= p2
+                  unless a2 == 0
+                    write_user("REQ_UNASSIGN #{d}", c2.username)
+                  end
+                else
+                  unless a1 == 0
+                    write_user("REQ_UNASSIGN #{d}", c1.username)
+                  end
+
+                end
+              }
+            }
+          end
+
+        end
+      end
+    }
+
+  end # of specbot_dupecheck
+
 	# searches through our hash of saved connections and writes them a messages.
 	def	write_user(input, username = nil)
 		if username.nil? || username.empty?
@@ -792,7 +981,7 @@ module CentralProtocolHandler
 				write_role($default_role, "PARTED User '#{@username}' leaves the party.", @username)
 				return "bye"
 
-			###now for the good stuff, broadcasting
+			### now for the good stuff, broadcasting role
 			elsif cmd == "REQ_BC"
 
 				# help with format .. but send the raw payload
@@ -878,24 +1067,7 @@ module CentralProtocolHandler
 				end
   		#end of REQ_BC here..
 
-      elsif cmd == "REQ_DNS"
-        # help with format .. but send the raw payload
-        if payload =~ /^(\d+\.\d+\.\d+\.\d+):(\d{1,5})/
-          iphost = $1
-          ipport = $2
-
-          targetname = $gs.get_cool_dns(iphost, ipport)
-
-          write_user("DNS_RE #{iphost}:#{ipport} #{targetname}") # write back to asking user.
-
-        else # of payload has format
-          write_user("SYS Command format is REQ_DNS <serverip>:<serverport>")
-          error = 1
-        end # of if payload has format
-      #end of REQ_DNS here..
-
-
-        elsif cmd == "BC_RE"
+      elsif cmd == "BC_RE"
 
 				if payload =~ /^(.+) (.+)=(\d+),(.+)=(\d+)$/
 
@@ -920,9 +1092,235 @@ module CentralProtocolHandler
 
 				else # of bc_re format check
 					put_log "SYS Format is BC_RE <bcid> <userstring>=<usercount>,<itemstring>=<itemcount>"
-				end
+        end
 
-			end
+      ### the specbot-admin ROLE does ...
+      elsif cmd == "REQ_ASSIGN"
+        if payload =~ /^([a-zA-Z_\.]+) (\d+\.\d+\.\d+\.\d+:\d{1,5})[,]?(true|false)?/
+          specbot = $1
+          hostport = $2
+          sp = $3
+          if sp == "true"
+            specbot_set_sp(specbot, hostport, sp)
+            write_user("REQ_ASSIGN #{hostport},#{sp}", specbot)
+          else
+            write_user("REQ_ASSIGN #{hostport}", specbot)
+          end
+        else # of format check
+          write_user("SYS Format is REQ_ASSIGN <specbot> <ip:port>[,<s-p;true or false>]")
+        end
+
+      elsif cmd == "REQ_UNASSIGN"
+        if payload =~ /^([a-zA-Z_\.]+) (\d+\.\d+\.\d+\.\d+:\d{1,5})/
+          specbot = $1
+          hostport = $2
+          write_user("REQ_UNASSIGN #{hostport}", specbot)
+        else # of format check
+          write_user("SYS Format is REQ_UNASSIGN <specbot> <ip:port>")
+        end
+
+      elsif cmd == "REQ_PING"
+        if payload =~ /^([a-zA-Z_\.]+) (\d+\.\d+\.\d+\.\d+:\d{1,5})/
+          specbot = $1
+          hostport = $2
+          write_user("REQ_PING #{hostport}", specbot)
+        else # of format check
+          write_user("SYS Format is REQ_PING <specbot> <ip:port>")
+        end
+
+      elsif cmd == "REQ_ASSIGNMENTS"
+        if payload =~ /^([a-zA-Z_\.]+)/
+          specbot = $1
+          write_user("REQ_ASSIGNMENTS give me your assignments", specbot)
+        else # of format check
+          write_user("SYS Format is REQ_ASSIGNMENTS <specbot>")
+        end
+
+      elsif cmd == "REQ_MAXSERVERS"
+        if payload =~ /^([a-zA-Z_\.]+)/
+          specbot = $1
+          write_user("REQ_MAXSERVERS how many do you do?", specbot)
+        else # of format check
+          write_user("SYS Format is REQ_MAXSERVERS <specbot>")
+        end
+
+      elsif cmd == "DUPECHECK"
+        write_user("SYS specbot server monitoring dupecheck started")
+        # start the central function for dupechecking:
+        specbot_dupecheck()
+
+      elsif cmd == "ELECTION"
+        write_user("SYS specbot server ping election on global server list started")
+        # start the central function for server election:
+        all_servers = specbot_servers()
+        put_log("#{all_servers}")
+        write_user("SYS active unique server count: #{all_servers.size}")
+
+        conns = connections_by_role("specbot")
+
+        #für jeden server, jeden ping der einzelnen bots vergleichen..
+        #falls ping info noch nicht vorhanden (>5000), dann einfordern
+        all_servers.each_key{|k|
+
+          put_log("SYS bots race for #{k} now..")
+
+          conns.each{|c|
+            ping = c.specbot_ping(k)
+            if ping > 5000
+              write_user("REQ_PING #{k}", c.username)
+            end
+          }
+
+          EventMachine.add_timer 5, proc {
+            lastbest = 5000
+            winner = ""
+            conns.each{|c|
+              ping = c.specbot_ping(k)
+              put_log("SYS #{ping} of #{c.username} to #{k}")
+              if ping < lastbest
+                lastbest = ping
+                winner = c
+                put_log("SYS - current best bot for #{k} is #{c.username}")
+              end
+            }
+            put_log("SYS --> best bot for #{k} is #{winner.username}")
+
+            conns.each{|c|
+              a = c.specbot_active?(k)
+              unless c == winner
+                unless a == 0
+                  write_user("REQ_UNASSIGN #{k}", c.username)
+                end
+              end
+              if c == winner
+                unless a == 1
+                  as = all_servers[k]
+                  sp = as.fetch("s-p")
+                  if sp == "true" || sp == "1"
+                    write_user("REQ_ASSIGN #{k},true", c.username)
+                  else
+                    write_user("REQ_ASSIGN #{k}", c.username)
+                  end
+
+                end
+
+              end
+            }
+
+          } # end of timer
+
+        } # end of all active servers loop
+
+      ### the specbot ROLE does ...
+      elsif cmd == "ASSIGNMENTS_RE"
+
+        if payload =~ /^(\d+\.\d+\.\d+\.\d+:\d{1,5}),(.*),(.*)/
+          hostport = $1
+          sp = $2
+          ping = $3.chomp
+
+          p hostport
+          p sp
+          p ping
+
+          current = Hash.new()
+          current.store("active", 1)
+          current.store("s-p", sp)
+          current.store("ping", ping)
+
+          # save the hash current to the hash @my_servers
+          @my_servers[hostport] = @my_servers[hostport].merge(current)
+          p @my_servers
+
+          # save new hash to a config file or sth. fixme
+
+
+        end
+      #end of ASSIGNMENTS_RE here..
+
+      elsif cmd == "MAXSERVERS_RE"
+        if payload =~ /^(\d+)/
+          @my_maxservers = $1
+        end
+      #end of MAXSERVERS_RE here..
+
+      elsif cmd == "PING_RE"
+        if payload =~ /^(\d+\.\d+\.\d+\.\d+:\d{1,5}),(.*)/
+          hostport = $1
+          ping = $2
+
+          current = Hash.new()
+          current.store("ping", ping)
+
+          # save the hash current to the hash @my_servers
+          @my_servers[hostport] = @my_servers[hostport].merge(current)
+          p @my_servers
+
+          # save new hash to a config file or sth. fixme
+        end
+
+      #end of PING_RE here..
+
+      elsif cmd == "ASSIGN_RE"
+        #assign_re should only be issued when a req_assign was issued to the specbot before
+        if payload =~ /^(\d+\.\d+\.\d+\.\d+:\d{1,5}) ([A-Z]+)[\s]?(.*)/
+          hostport = $1
+          good = $2
+          reason = $3
+          if good == "OK"
+            current = Hash.new()
+            current.store("active", 1)
+            @my_servers[hostport] = @my_servers[hostport].merge(current) # this way it preserves previously saved ping values
+
+            write_user("REQ_PING #{hostport}")
+
+            put_log "SYS #{username} assigned to #{hostport} and asked ping for it."
+            write_role("specbot_admin", "SYS #{username} assigned to #{hostport} and asked ping for it.")
+          else
+            put_log "SYS #{username} failed to assign #{hostport}, reason: '#{reason}'"
+            write_role("specbot_admin", "SYS #{username} failed to assign #{hostport}, reason: '#{reason}'")
+          end
+        end
+      #end of ASSIGN_RE here..
+
+      elsif cmd == "UNASSIGN_RE"
+        if payload =~ /^(\d+\.\d+\.\d+\.\d+:\d{1,5}) ([A-Z]+)[\s]?(.*)/
+          hostport = $1
+          good = $2
+          reason = $3
+          if good == "OK"
+            current = Hash.new()
+            current.store("active", 0)
+            @my_servers[hostport] = @my_servers[hostport].merge(current) # this way it preserves previously saved ping values
+            put_log "SYS #{username} unassigned #{hostport}"
+            write_role("specbot_admin", "SYS #{username} unassigned #{hostport}")
+          else
+            put_log "SYS #{username} failed to unassign #{hostport}, reason: '#{reason}'"
+            write_role("specbot_admin", "SYS #{username} failed to unassign #{hostport}, reason: '#{reason}'")
+          end
+
+        end
+      #end of UNASSIGN_RE here..
+
+      elsif cmd == "REQ_DNS"
+        # help with format .. but send the raw payload
+        if payload =~ /^(\d+\.\d+\.\d+\.\d+):(\d{1,5})/
+          iphost = $1
+          ipport = $2
+
+          targetname = $gs.get_cool_dns(iphost, ipport)
+
+          write_user("DNS_RE #{iphost}:#{ipport} #{targetname}") # write back to asking user.
+
+        else # of payload has format
+          write_user("SYS Command format is REQ_DNS <serverip>:<serverport>")
+          error = 1
+        end # of if payload has format
+      #end of REQ_DNS here..
+
+
+
+      end
 
 
     else  # of if allowed command
@@ -987,6 +1385,25 @@ module CentralProtocolHandler
             write_user("ROLES #{my_roles.join(", ")}")
             write_user("COMMANDS #{my_cmds.join(", ")}")
             write_role($default_role, "JOINED User '#{@username}' just joined the party.", @username)
+
+            # if that guy is a specbot, ask it for his current list
+            if my_roles.include?("specbot")
+              EventMachine.add_timer 2, proc {
+                write_user("REQ_MAXSERVERS how many can you do?")
+                write_user("REQ_ASSIGNMENTS gimme all your servers")
+              }
+
+              # if more than 1 specbots are connected, then we want to activate the dupe-check.
+              if connections_by_role("specbot").size > 1
+                # in 6 seconds we start it. by then, we should have the full serverlists of all bots
+                EventMachine.add_timer 6, proc {
+                  put_log("this guy: #{username} triggers the dupecheck now")
+                  specbot_dupecheck
+                }
+              end
+
+            end
+
           else
             put_log "SYS User '#{username}' unknown to me. Saying bye."
             send_data "SYS User '#{username}' unknown to me. Bye.\n"