diff --git a/app/models/server.rb b/app/models/server.rb index 5154198..6962480 100644 --- a/app/models/server.rb +++ b/app/models/server.rb @@ -31,7 +31,6 @@ # category_id :integer # -require "rcon" require "yaml" class Server < ActiveRecord::Base @@ -40,12 +39,8 @@ class Server < ActiveRecord::Base DOMAIN_HLDS = 0 DOMAIN_HLTV = 1 DOMAIN_NS2 = 2 - HLTV_IDLE = 1200 - DEMOS = "/var/www/virtual/ensl.org/hlds_l/ns/demos" - QSTAT = "/usr/bin/quakestat" - TMPFILE = "tmp/server.txt" - attr_accessor :rcon_handle, :pwd + attr_accessor :pwd attr_protected :id, :user_id, :updated_at, :created_at, :map, :players, :maxplayers, :ping, :version validates_length_of [:name, :dns,], :in => 1..30 @@ -71,301 +66,124 @@ class Server < ActiveRecord::Base AND match_time > '#{(time.ago(Match::MATCH_LENGTH).utc).strftime("%Y-%m-%d %H:%M:%S")}' AND match_time < '#{(time.ago(-Match::MATCH_LENGTH).utc).strftime("%Y-%m-%d %H:%M:%S")}'", :conditions => "matches.hltv_id IS NULL"} } - scope :of_addr, - lambda { |addr| { - :conditions => { - :ip => addr.match(/([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})/)[0], - :port => addr.match(/:([0-9]{1,5})/)[1] } } } - scope :of_category, - lambda { |category| { - :conditions => {:category_id => category.id} }} - has_many :logs - has_many :matches - has_many :challenges - belongs_to :user - belongs_to :recordable, :polymorphic => true + has_many :logs + has_many :matches + has_many :challenges + belongs_to :user + belongs_to :recordable, :polymorphic => true - before_create :set_category + before_create :set_category - acts_as_versioned - non_versioned_columns << 'name' - non_versioned_columns << 'description' - non_versioned_columns << 'dns' - non_versioned_columns << 'ip' - non_versioned_columns << 'port' - non_versioned_columns << 'rcon' - non_versioned_columns << 'password' - non_versioned_columns << 'irc' - non_versioned_columns << 'user_id' - non_versioned_columns << 'official' - non_versioned_columns << 'domain' - non_versioned_columns << 'reservation' - non_versioned_columns << 'recording' - non_versioned_columns << 'idle' - non_versioned_columns << 'default_id' - non_versioned_columns << 'active' - non_versioned_columns << 'recordable_type' - non_versioned_columns << 'recordable_id' + acts_as_versioned + non_versioned_columns << 'name' + non_versioned_columns << 'description' + non_versioned_columns << 'dns' + non_versioned_columns << 'ip' + non_versioned_columns << 'port' + non_versioned_columns << 'rcon' + non_versioned_columns << 'password' + non_versioned_columns << 'irc' + non_versioned_columns << 'user_id' + non_versioned_columns << 'official' + non_versioned_columns << 'domain' + non_versioned_columns << 'reservation' + non_versioned_columns << 'recording' + non_versioned_columns << 'idle' + non_versioned_columns << 'default_id' + non_versioned_columns << 'active' + non_versioned_columns << 'recordable_type' + non_versioned_columns << 'recordable_id' - def domains - {DOMAIN_HLTV => "HLTV", DOMAIN_HLDS => "NS Server", DOMAIN_NS2 => "NS2 Server"} - end - - def to_s - name - end - - def addr - ip + ":" + port.to_s - end - - def players_s - if players.nil? or max_players.nil? - "N/A" - else - players.to_s + " / " + max_players.to_s - end - end - - def recording_s - return nil if self.domain != DOMAIN_HLTV - # recording.to_i > 0 ? Match.find(recording).to_s : recording - recording - end - - def reservation_s - return nil if domain != DOMAIN_HLTV - reservation - end - - def graphfile - File.join("public", "images", "servers", id.to_s + ".png") - end - - def set_category - self.category_id = (domain == DOMAIN_NS2 ? 45 : 44 ) - end - - def after_validation_on_update - if reservation_changed? - rcon_connect - if reservation == nil - rcon_exec "stop" - self.recording = nil - self.recordable = nil - self.idle = nil - save_demos if self.recording - hltv_stop - else - if changes['reservation'][0].nil? - hltv_start - rcon_exec "stop" - self.recording = recordable.demo_name if recordable and recordable_type == "Match" or recordable_type == "Gather" - rcon_exec "record demos/" + self.recording if self.recording - end - rcon_exec "serverpassword " + pwd - rcon_exec "connect " + reservation - self.idle = DateTime.now - end - rcon_disconnect - end - end - - def execute command - rcon_connect - response = rcon_exec command - rcon_disconnect - response - end - - def rcon_connect - self.rcon_handle = RCon::Query::Original.new(ip, port, rcon) - end - - def rcon_exec command - response = rcon_handle.command(command) - - Log.transaction do - Log.add(self, Log::DOMAIN_RCON_COMMAND, command) - if response.to_s.length > 0 - Log.add(self, Log::DOMAIN_RCON_RESPONSE, response) - end - end - - response - end - - def rcon_disconnect - rcon_handle.disconnect - end - - def hltv_start - if nr = hltv_nr - `screen -d -m -S "Hltv-#{nr[1]}" -c $HOME/.screenrc-hltv $HOME/hlds_l/hltv -ip 78.46.36.107 -port 28#{nr[1]}00 +exec ns/hltv#{nr[1]}.cfg` - sleep 1 - end - end - - def hltv_nr - self.name.match(/Tv \#([0-9])/) - end - - def hltv_stop - if nr = hltv_nr - sleep 5 - rcon_exec "exit" - #`screen -S "Hltv-#{nr[1]}" -X 'quit'` - end - end - - def save_demos - dir = case recordable_type - when "Match" then - recordable.contest.demos - when "Gather" then - Directory.find(Directory::DEMOS_GATHERS) - end - - dir ||= Directory.find(Directory::DEMOS_DEFAULT) - zip_path = File.join(dir.path, recording + ".zip") - - Zip::ZipOutputStream::open(zip_path) do |zos| - if recordable_type == "Match" - zos.put_next_entry "readme.txt" - zos.write "Team1: " + recordable.contester1.to_s + "\r\n" - zos.write "Team2: " + recordable.contester2.to_s + "\r\n" - zos.write "Date: " + recordable.match_time.to_s + "\r\n" - zos.write "Contest: " + recordable.contest.to_s + "\r\n" - zos.write "Server: " + recordable.server.addr + "\r\n" if recordable.server - zos.write "HLTV: " + addr + "\r\n" - zos.write YAML::dump(recordable.attributes).to_s - end - Dir.glob("#{DEMOS}/*").each do |file| - if File.file?(file) and file.match(/#{recording}.*\.dem/) - zos.put_next_entry File.basename(file) - zos.write(IO.read(file)) - end - end - end - - DataFile.transaction do - unless dbfile = DataFile.find_by_path(zip_path) - dbfile = DataFile.new - dbfile.path = zip_path - dbfile.directory = dir - dbfile.save! - DataFile.update_all({:name => File.basename(zip_path)}, {:id => dbfile.id}) - end - if recordable_type == "Match" - recordable.demo = dbfile - recordable.save - end - end - end - - def make_stats - graph = Gruff::Line.new - graph.title = name - pings = [] - players = [] - labels = {} - n = 0 - - for version in versions.all(:order => "updated_at DESC", :limit => 30).reverse - pings << version.ping.to_i - players << version.players.to_i - labels[n] = version.updated_at.strftime("%H:%M") if n % 3 == 0 - n = n + 1 - end - - graph.theme_37signals - graph.data("Ping", pings, '#057fc0') - graph.data("Players", players, '#ff0000') - graph.labels = labels - graph.write(graphfile) - end - - def default_record - # if self.default - # rcon_exec "record demos/auto-" + Verification.uncrap(default.name) - # rcon_exec "serverpassword " + default.password - # rcon_exec "connect " + default.addr - # end -end - -def is_free time - challenges.around(time).pending.count == 0 and matches.around(time).count == 0 -end - -def can_create? cuser - cuser -end - -def can_update? cuser - cuser and cuser.admin? or user == cuser -end - -def can_destroy? cuser - cuser and cuser.admin? -end - -def self.refresh - servers = "" - Server.hlds.active.all.each do |server| - servers << " -a2s " + server.ip + ":" + server.port.to_s + def domains + {DOMAIN_HLTV => "HLTV", DOMAIN_HLDS => "NS Server", DOMAIN_NS2 => "NS2 Server"} end - file = File.join(Rails.root, TMPFILE) - system "#{QSTAT} -xml #{servers} | grep -v '' > #{file}" + def to_s + name + end - doc = REXML::Document.new(File.new(file).read) - doc.elements.each('qstat/server') do |server| - hostname = server.elements['hostname'].text.split(':', 2) - if s = Server.active.first(:conditions => {:ip => hostname[0], :port => hostname[1]}) - if server.elements.include? 'map' - s.map = server.elements['map'].text - s.players = server.elements['numplayers'].text.to_i - s.max_players = server.elements['maxplayers'].text.to_i - s.ping = server.elements['ping'].text - s.map = server.elements['map'].text - s.save - s.make_stats + def addr + ip + ":" + port.to_s + end + + def set_category + self.category_id = (domain == DOMAIN_NS2 ? 45 : 44 ) + end + + def save_demos + dir = case recordable_type + when "Match" then + recordable.contest.demos + when "Gather" then + Directory.find(Directory::DEMOS_GATHERS) + end + + dir ||= Directory.find(Directory::DEMOS_DEFAULT) + zip_path = File.join(dir.path, recording + ".zip") + + Zip::ZipOutputStream::open(zip_path) do |zos| + if recordable_type == "Match" + zos.put_next_entry "readme.txt" + zos.write "Team1: " + recordable.contester1.to_s + "\r\n" + zos.write "Team2: " + recordable.contester2.to_s + "\r\n" + zos.write "Date: " + recordable.match_time.to_s + "\r\n" + zos.write "Contest: " + recordable.contest.to_s + "\r\n" + zos.write "Server: " + recordable.server.addr + "\r\n" if recordable.server + zos.write "HLTV: " + addr + "\r\n" + zos.write YAML::dump(recordable.attributes).to_s + end + Dir.glob("#{DEMOS}/*").each do |file| + if File.file?(file) and file.match(/#{recording}.*\.dem/) + zos.put_next_entry File.basename(file) + zos.write(IO.read(file)) + end + end + end + + DataFile.transaction do + unless dbfile = DataFile.find_by_path(zip_path) + dbfile = DataFile.new + dbfile.path = zip_path + dbfile.directory = dir + dbfile.save! + DataFile.update_all({:name => File.basename(zip_path)}, {:id => dbfile.id}) + end + if recordable_type == "Match" + recordable.demo = dbfile + recordable.save end end end - servers = "" - Server.hltvs.reserved.each do |server| - servers << " -a2s #{server.reservation}" + def is_free time + challenges.around(time).pending.count == 0 and matches.around(time).count == 0 end - doc = REXML::Document.new(`#{QSTAT} -xml #{servers} | grep -v ''`) - doc.elements.each('qstat/server') do |server| - hostname = server.elements['hostname'].text.split(':', 2) - if s = Server.hltvs.reserved.first(:conditions => {:ip => hostname[0], :port => hostname[1]}) - if server.elements['numplayers'].text.to_i > 0 - s.update_attribute :idle, DateTime.now - elsif (s.idle + HLTV_IDLE).past? - s.reservation = nil - s.save - end + def can_create? cuser + cuser + end + + def can_update? cuser + cuser and cuser.admin? or user == cuser + end + + def can_destroy? cuser + cuser and cuser.admin? + end + + def self.move addr, newaddr, newpwd + self.hltvs.all(:conditions => {:reservation => addr}).each do |hltv| + hltv.reservation = newaddr + hltv.pwd = newpwd + hltv.save! + end + end + + def self.stop addr + self.hltvs.all(:conditions => {:reservation => addr}).each do |hltv| + hltv.reservation = nil + hltv.save! end end end - -def self.move addr, newaddr, newpwd - self.hltvs.all(:conditions => {:reservation => addr}).each do |hltv| - hltv.reservation = newaddr - hltv.pwd = newpwd - hltv.save! - end -end - -def self.stop addr - self.hltvs.all(:conditions => {:reservation => addr}).each do |hltv| - hltv.reservation = nil - hltv.save! - end -end -end