Warning: include_once(/var/www/html/pmwiki-2.2.86/cookbook/soap4pmwiki/soap4pmwiki.php): failed to open stream: No such file or directory in /var/www/html/fields/dbp13/local/config.php on line 4

Warning: include_once(): Failed opening '/var/www/html/pmwiki-2.2.86/cookbook/soap4pmwiki/soap4pmwiki.php' for inclusion (include_path='.:/opt/php/lib/php') in /var/www/html/fields/dbp13/local/config.php on line 4

Warning: Cannot modify header information - headers already sent by (output started at /var/www/html/fields/dbp13/local/config.php:4) in /var/www/html/pmwiki-2.2.86/pmwiki.php on line 1250
Datenbankpraktikum SS 2013 - MMOG - Planeten Und Gebaeude Hintergrundprozesse
?

Hintergrundprozesse
Gebäudebau und Rohstoffproduktion

Henning Krömker

Problemstellung / Aufgabe

Sobald ein User einen Planeten in Besitz genommen hat, sollte er die Möglichkeit bekommen, auf seinem Planeten Gebäude zu bauen. In einem MMOG ist es üblich, dass die einzelnen Gebäude verschiedene Bauzeiten haben und natürlich auch nicht aus dem Nichts in wenigen Millisekunden entstehen sollten. Des weiteren musste auch ein Prozess geschaffen werden, der die Ressourcen eines Planeten, je nach Vorhandensein der benötigten Gebäude, erhöht.

Lösung mittels Resque und Resque-scheduler

Unsere Lösung dies zu bewerkstelligen ist Resque. Resque ist eine Redis backend Bibliothek und wird als gem in Ruby bereitgestellt. Dieses gem ermöglicht Hintergrundprozesse parrallell über sogenannte 'Worker' ablaufen zu lassen und über den Resque-scheduler einen Zeitstempel mitzugeben, sodass diese Prozesse zeitversetzt erledigt werden. Die Worker sind in Ruby nichts anderes als Klassen, die bei Aufruf die bereitgestellten Methoden des Planeten aufrufen und nach Ablauf der im Aufruf geforderten Zeit ausführen.

Implementierung

Unsere zwei Hintergrundprozesse sind in der Idee unterschiedlich. Während der Gebäudebau vom User aufgerufen werden kann und nach Ausführung beendet ist, soll der Prozess zum erhöhen der Ressourcen ständig wiederholt werden.

Konfiguration

Zunächst ist es notwendig, die benötigeten gems in das Gemfile einzufügen

gem resque
gem resque-scheduler

Mit dem Aufruf bundle install, werden die neuen gems nun installiert Außerdem benötigt Resque den Redis-server, welcher auf http://redis.io/download zum Download zur verfügung steht. Über den Terminal starten wir nun den Worker und den Scheduler mittels

rake resque:work QUEUE=*
rake resque:scheduler

Modell

Ressourcenproduktion

Der Planet stellt uns bereits Methoden bereit, die im Fall dass ein Planet gewisse Gebäude besitzt, die Ressourcen des Planeten ständig erhöht werden. Methoden in planet.rb:

  • 'get_production', ist eine get Methode, die den Produktionswert eines Gebäudes berechnet und diesen zurück gibt. Der Wert berechnet sich aus einem Forschungswert, der bei höherer Forschungsstufe erhöht wird und dem Attribut 'production', welches die Gebäudetypen bereitstellen.
  • 'update_ressources', ist nun die Methode, die die Ressourcen eines Planeten erhöht. Bedingung dieser Methode ist zunächst, dass die Energievorräte auf dem Planeten größer Null sind. Ist dies der Fall, so wird mittels get_production ermittelt, wie viel produziert werden soll und dies auf den aktuellen Bestand der Ressource aufsummiert. Die Bedingung, dass die Energievorräte des Planeten größer Null sind, gilt jedoch nicht für die Produktion der Energie, um sicherzustellen, dass unser MMOG spielbar bleibt.
    Am Ende der Methode wird dann die 'create_production_job' methode ausgeführt. Dies ist sozusagen ein rekursiver Aufruf der update_ressources Methode, sodass sich dieser Prozess nach einem gewissen Zeitraum wiederholt.
  • 'create_production_job', ruft den Resque-worker auf. Hier wird die Zeit mit der der Worker zeitversetzt aufgerufen werden soll, der Name des Workers und die benötigten Attribute mitgegeben.
    Resque.enqueue_in(30.second, ProduceResources, self.id)

Der Worker, produce_ressources.rb:

  • Ein worker bekommt nur die self.perform Methode, welche die benötigten Attribute übergeben bekommt.
class ProduceResources
    @queue = "workqueue"
    def self.perform(id)
        @planet = Planet.find_by_id(id)
        @planet.update_resources
        @planet.save
    end
end

Gebäudebau

Ein Planet kann bis zu acht Gebäude besitzen. Auch hier sind die Methoden zum Bau im Planeten gegeben.

Planet.rb:

  • 'build_building', erstellt das gewünschte Gebäude auf dem Planeten.
  • 'create_building_job', startet den Worker zum Erstellen eines Gebäudes.
Resque.enqueue_in(built_time.second, BuildBuilding, id_array)

Die Gebäude sollten unterschiedliche Bauzeiten bekommen, sodass ein Gebäude Level 5 deutlich mehr Zeit verbraucht als ein Gebäude Level 1. Dies haben wir durch ein zusätzliches Attribut im Buildingtype gelöst, welches jedem Gebäude eine konstante Zeit zuordnet. Außerdem reicht es nun nicht mehr aus dem Worker bloß die Planet ID mitzugeben, da dieser auch wissen muss, welches Gebäude er bauen bzw. upgraden soll. Desshalb übergeben wir dem Worker nun ein Array, in dem die Planet ID und die Gebäude ID gespeichert sind.

Der Worker, BuildBuilding.rb, sieht nun wie folgt aus:

class BuildBuildings
    @queue = "buildqueue"
    def self.perform(id_array)
        planet = Planet.find(id_array[0])
        planet.build_building(id_array[1])
    end
end

Abbruch des Workers

Natürlich soll es auch möglich sein, den Gebäudebau abzubrechen. Auch dies bietet Resque an, man benötigt allerdings die übergebenen Attribute, um den genauen Prozess identifizieren zu können.

Im planet.rb existiert nun auch die Methode 'delete_building_job':

(:code) Resque.remove_delayed(BuildBuildings, id_array) (:endcode:)

Controller

Um den Gebäudebau in der Planetview darstellen zu können, mussten wir den PlanetController ein wenig anpassen. Wir benötigten Kontrollstrukturen für den Bau eines Gebäudes, sowie auch für den Abbruch eines Workers. So bekam der PlanetController von uns noch die Methoden upgrade_building und abort_upgrade. Hier musste nun sichergestellt werden, dass nur User auf einem Planeten bauen können, wenn dieser ihnen auch zugeordnet ist.

@planet = Planet.find(params["id"])
if @planet.user.id == current_user.id then
if @planet.create_building_job(params["btype"].to_sym) then
[…]

Außerdem muss kontrolliert werden, dass der User auf seinem Planeten gerade überhaupt bauen darf. Zum Abbrechen eines Bauauftrags, muss lediglich kontrolliert werden, dass auch gerade ein Gebäude auf dem Planeten gebaut wird.

View

Da die Gebäude in der Planetview dargestellt werden, war die Idee, einen 'Onclick-Event' hinzuzufügen, bei dem Informationen zum Gebäude, wie Level, aktuelle und zukünftige Produktion, Kosten und Bauzeit eines Upgrades bzw. die notwendigen Vorraussetzungen für ein Upgrade anzeigen.
Für die Hintergrundprozesse wollten wir Buttons hinzufügen, die den BuildBuilding Worker starten bzw. abbrechen.

Den dynamischen Prozessbalken, stellt Twitter Bootstrap uns bereit. Die einzige Problematik hierbei lag darin, die Start bzw. die Endzeit des Bauprozesses zu ermitteln. Hierfür haben wir dem Planeten ein weiteres Attribut start_construction_at mitgegeben. Da im buildingtype.rb die built_time der einzelnen Gebäude gespeichert ist, ließ sich nun auch die Endzeit sehr einfach berechnen.

Fazit

Das Ziel Deadlocks zu vermeiden, haben wir durch den Einsatz des Resque gems und unserer Spielregeln, erreichen können. Der Resque-scheduler prozessiert die einzelnen Aufträge in ihrer angegebenen Reihenfolge. Dem Spieler wird immer nur ein Gebäudebau gleichzeitig erlaubt, wodurch wir Deadlocks nun ausschließen können sollten.


Page last modified on August 24, 2013, at 01:02 PM