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 - Technologien Model-View-Controller

Model

- Jonas Rothe -

Zunächst soll die Beziehung zwischen User und Technology hergestellt werden. Da user_technologies eine n:m - Beziehung mit zusätzlichen Attributen ist, musste diese als zusätzliches Model implementiert werden.

technology.rb

   has_and_belongs_to_many :users
   has_many :user_technologies
   has_many :users, :through => :user_technologies

user_technologies.rb

   belongs_to :user
   belongs_to :technology

user.rb

   has_many :user_technologies
   has_many :technologies, :through => :user_technologies

   has_one :user_setting, :dependent => :destroy

user_setting.rb

   belongs_to :user

Da, wie schon im Datenbank-Schema genannt, viele Spielbereiche direkt auf die UserSettings zugreifen, haben wir, um Nullpointer zu verhindern, mit Hilfe eines Callbacks im user-Model sichergestellt, dass, sobald ein User sich registriert, auch ein Eintrag in den UserSettings mit Default-Werten existiert.

   after_create: init_usersettings
   def init_usersettings
     UserSetting.create(:user => self)
   end

Um schnell auf die einzelnen Attribute von UserSetting zugreifen zu können, gibt es für jedes Attribut sogenannte get- bzw. has-Methoden, die ebenfalls im user-Modell zu finden sind.

   def get_power
     self.user_setting.increased_current_userpower
   end
   ...
   def has_deathstar?
    self.user_setting.deathstar
   end

Zur einfacheren Fehlerbehandlung und Wiederverwendbarkeit haben wir uns zunächst überlegt, welche Gründe es gibt, warum eine bestimmte Technologie nicht erforschbar ist und für jeden dieser Gründe eine eigene Methode definiert, die dementsprechend true oder false zurückgibt. Anschließend haben wir uns Gedanken darum gemacht, welches die häufigsten Grunde und welches die aufwändigsten Abfragen sind, um so effizient wie möglich ein Resultat zu erlangen. Aufgrund diesen Überlegungen werden die folgenden Methode, sortiert nach Häufigkeit, in dieser Reihenfolge aufgerufen:

  1. is_researching? - ist der Spieler gerade am forschen?
  2. building_rank_require? - besitzt der Spieler ein Forschungszentrum der benötigten Stufe?
  3. max_rank? - hat der Spieler schon die höchste Stufe erforscht?
  4. cost_require? - hat der Spieler genug Spacecash zum forschen?
  5. tech_require? - hat der Spieler alle voraussetzenden Technologien erforscht?

Wird nun die Methode zum Erforschen durch den Controller aufgerufen, werden im Model abermals sämtliche Voraussetzungen überprüft. Dies mag auf den ersten Blick unnötig erscheinen, da die View den Button zum Forschen nur anzeigt, wenn alle Bedingungen erfüllt sind, jedoch könnte durch ein modifiziertes POST-Statement dieser Schutz umgangen werden oder gar eine andere Technologie erforscht werden, die noch gar nicht freigeschaltet ist.

Sind die Voraussetzungen gegeben, wird in das Attribut researching in UserSetting die zu erforschende Technologie-ID eingetragen und somit für diesen Spieler eine weitere Forschung unterbunden.

Anschließend werden die Kosten für die Technologie dem Spieler abgezogen.

Um nun eine Forschungsdauer zu simulieren haben wir das Ruby on Rails gem Resque verwendet. Dies ermöglicht es mit dem Befehl enqueue_in, welcher als Übergabeparameter die Verzögerung und die aufzurufende Klasse benötigt, sowie weitere Parameter, die in der aufzurufenden Klasse angegeben werden.

Die Klasse für den Job enthält die Variable @queue, in der der Name für den Auftrag angegeben wird, sowie eine statische Methode perform(args**)

Für unsere Forschung haben wir dementsprechend im jobs Ordner die Klasse research_technology.rb angelegt:

 class ResearchTechnology	
   @queue = "research_technology"  		//Auftragsname
   def self.perform(user, tech)			
     Technology.find(tech).update_uservalues(user)
   end
 end

Dieser kann nun von überall wie folgt aufgerufen werden:

   Resque.enqueue_in(duration.second, ResearchTechnology, user.id, self.id )

Dadurch wird der Job dem Scheduler von Resque übergeben, der ihn zur rechten Zeit einem sogenannten Worker übergibt.

Wenn nun der Job ausgeführt wird, wird der Inhalt der perform-Methode abgearbeitet. In diesem Fall wird die Methode update_uservalues aufgerufen, die nun die, Aufgrund des erfolgreichen Abschlusses der Forschung, neuen gültigen Werte für den Spieler in der Datenbank festhält und die Forschung wieder ermöglicht.

Für den Fall, dass eine Forschung vorzeitig abgebrochen werden soll, kann man mit folgendem Befehl den Auftrag wieder aus dem Scheduler austragen:

   Resque.remove_delayed(ResearchTechnology, user.id, id)

View

- Ronja Uder -

Wir wollten mithilfe unserer View einen möglichst unkomplizierten und intuitiven Überblick über die Möglichkeiten beim Erforschen von Technologien in Cypis bieten .

Um dies zu erreichen haben wir uns für ein einfaches Tabellen-Layout entschieden, welches übersichtlich die verfügbaren Technologien darstellt.

So wird die Tabelle der Technologien in der View angezeigt, wenn alle Voraussetzungen erfüllt sind und die Technologie erforscht werden kann

Im linken Tabellenfeld wird das Icon der Technologie angezeigt, das mittlere Feld gibt einen Überblick über die Entsprechende Technologie,inklusive der Voraussetzungen und der berechneten Dauer und Kosten einer Technologie, daneben ist die momentane Stufe der Forschung zu sehen und im rechten Feld sieht man den Status der Forschung.

  <% TechnologyRequire.all.map(&:building_rank).uniq.each do |rank| %>
    <tr><th colspan="4"><%= "Forschungszentrum Stufe: "+ rank.to_s %></th></tr>
      <% TechnologyRequire.where(:building_rank => rank).map(&:tech_id).uniq.each do |technology| %>
        <% technology = Technology.find(technology) %>
          <tr>
            <td align="middle"  valign="middle"><img src="<%=TechnologiesHelper.get_icon_name(technology).to_s%>"></td>
            <td align="middle"  valign="middle"><strong><%= 'Name: ' %></strong><%= technology.title %><br>
              <strong><%= 'Benötigte Technologien: ' %></strong><%=technology.get_requirements %><br>
              <strong><%= 'Kosten: ' %></strong><%= technology.get_technology_cost(current_user)%><br>
              <strong><%= 'Dauer: ' %></strong><%= technology.get_research_duration_formatted(current_user).to_s + ' Sekunden'%> <br>
              <strong><%= 'Beschreibung:' %></strong><%= technology.description%>
            </td>

            <td align="middle"  valign="middle" width="50"><%= technology.get_technology_rank(current_user)%></td>

            ...#An dieser Stell findet im Code die Überprüfung der momentanen Verfügbarkeit einer Technologie statt

          </tr>
      <% end %>
  <% end %>

Um das jeweilige Icon einer Technologie richtig anzuzeigen zu können, haben wir uns im TechnologiesHelper eine kleine Hilfsmethode zum Generieren der Dateinamen geschrieben.

     def self.get_icon_name(tech)
      techname = Technology.find(tech).name
      filename = 'images/icons/' + techname.to_s+'_icon.png'
     end

Da wir nicht wollten, dass der User erst manuell nachprüfen muss, welche Technologien er gerade erforschen kann und ob er auch alle Voraussetzungen erfüllt hat, überprüfen wir bereits in der View, ähnlich wie beim Updaten einer Technologie, nacheinander alle Voraussetzungen die erfüllt werden müssen und geben sie dementsprechend in der Tabelle aus. Damit alle Informationen möglichst auf einen Blick ersichtlich sind, haben wir mithilfe einiger erstellten Css-Styles die Farbgebung der Statusausgabe angepasst, sodass man schon farblich erkennen kann, wie der momentane Zustand der Forschung ist.

            <%if technology.building_rank_require?(current_user) == false%>
              <td><p class="missing_research_lvl"><%='Fehlende Forschungsstufe' %></p></td>
            <%elsif technology.max_rank?(current_user)%>
              <td><p class="maxrank"><%='Maxrank erreicht' %></p></td>
            <%elsif technology.tech_require?(current_user)== false %>
              <td><p class="missing_tech"><%='Fehlende Voraussetzung' %></td>
            <%elsif  technology.cost_required?(current_user) == false %>
              <td><p class="missing_money"><%='Nicht genug Geld' %></p></td>
            <%elsif research_id != 0%>
              <td><p class="forscht"><%= 'Forscht.'%></p></td>
            <>
              <td align="middle"  valign="middle" width="100"><%= button_to 'Upgrade',
                {:controller => "technologies", :action => "upgrade",    :uid => current_user, :id => technology} ,
                :method => :post, data: { confirm: 'Möchtest du die Technologie "' + technology.title + '" aufwerten?' }%>
              </td>

An dieser Stelle war unsere View grundlegend fertig, es fehlte nur noch eine passende Visualisierung der Forschung selbst. Wir haben uns für einen Update-Balken, den Twitter-Bootstrap bereitstellt, entschieden.

Da wir aber keinen statischen Balken haben wollten, sondern einen der sich stetig aktualsiert, mussten wir den bereitgestellten Twitter-Bootstrap-Balken ein wenig anpassen. Damit der Fortschrittsbalken sich ändert ohne jedesmal die komplette Seite neu laden zu müssen, haben wir uns eines kleinen Javascripts bedient. Dieses fragt einmal beim Seitenaufbau das Forschungsanfang und -dauer ab, berechnet jede viertel Sekunde den bisherigen Fortschritt in Prozent neu und aktualisiert per AJAX das width-Attribut des Fortschrittsbalken.

   var gone = <%=Time.now.to_i - array[2].to_i %>;  //array[2] = Forschungsanfang
   var end  = <%=(array[3].to_i) +1%>;              //array[3] = Forschungsdauer

   function update_progressbar(){
     gone += 0.25;
     var p = (gone/end)*100;
     var d = document.getElementById('auto-updating-progressbar');
     d.style.width = (p+"%");
   };
   window.setInterval(update_progressbar, 250);

Da es schwierig ist, an dem Balken selbst eine genaue Zeit abzuschätzen, haben wir unter dem Balken noch den genauen Endzeitpunkt der Forschung eingebaut, damit der User jederzeit genau einsehen kann, wann seine Forschung abgeschlossen ist.

Um eine Forschung, die bereits gestartet ist abbrechen zu können, haben wir unter dem Forschungsbalken einen 'Abbrechen-Button' eingefügt, der auf die Methode abort im Controller verlinkt

Forschungsbalken mit Endzeitpunkt und Abbrechen-Button

Controller

- Jonas Rothe -

Da während des Spielens keine (neuen) Technologien erzeugt, gelöscht, editiert oder genauer angezeigt werden müssen, wird nur die index-Action des Controllers benötigt, dazu werden die anderen Methoden über die except-Klausel in der Datei routes.rb gesperrt.

   resources :technologies, :except => [:edit, :update, :create, :new, :destroy, :show]

Damit Technologien erforscht und auch abgebrochen werden können, haben wir je eine neue Methode (upgrade, abort) der Datei technologies_controller.rb hinzugefügt, die jeweils selbst die entsprechende Methode im Model aufrufen und die Seite aktualisieren, wodurch der Fortschrittsbalken sichtbar wird oder wieder verschwindet. Diese Methoden müssen ebenfalls mit ihrem HTTP-Request-Typ in der routes.rb eingetragen werden, um sie verwenden zu können.

   post 'technologies/upgrade' => 'technologies#upgrade'
   post 'technologies/abort'   => 'technologies#abort'

Außerdem muss der Spieler um Technologien zu erforschen eingeloggt sein. Dies wird mit Hilfe des Schlüsselworts before_filter ermöglich, welche am Anfang des Controllers eingetragen werden:

   before_filter :authenticate_user!


Page last modified on August 23, 2013, at 02:17 PM