• Login

Dear visitor, welcome to RPG Studio - Make your World real. If this is your first visit here, please read the Help. It explains in detail how this page works. To use all features of this page, you should consider registering. Please use the registration form, to register here or read more information about the registration process. If you are already registered, please login here.

  • Playm

    RPG Studio Webmaster

    You have to register first, to connect to this user.

315

Ein super einfaches Pluginsystem für Ruby-Apps entwickeln

Rating:

by Playm, Saturday, September 30th 2017, 10:31pm

Hier mal wieder ein kleiner Blogpost zum Wochenende. Viel Spaß, Freunde! :hi:

Es galt folgende Aufgabenstellung zu lösen. Gegeben war eine in Ruby geschriebene Anwendung qt_app.rb. Diese sollte jetzt vom Benutzer mit eigenen Bausteinen erweitert werden können, ohne das er aktiv die qt_app.rb-Datei umschreiben müsste. Ich entschied mich dafür ein einfaches Pluginsystem zu entwickeln. Das System war minimalistisch. Die App sollte einfach alle pluged-in Scripts laden und ausführen. Die Plugins sollten schon wissen, was sie tun.
Prinzipiell war das mit dem Scriptsystem im RPG Maker XP bis VX Ace zu vergleichen. Der Maker führte einfach hintereinander alle Scripts aus und hoffentlich kam dann ein feines Spiel bei raus.

Ich wollte aber keine Scripts.rxdata und einen Scripteditor zwischenschalten, sondern der Benutzer würde einfach Rubyscriptdateien in einen bestimmten Ordner legen oder wieder daraus entfernen. Im Grunde genommen nur Dateien verschieben. Das klang einfach und dem Benutzer zumutbar.

Werden wir mal konkret. Das hier ist der Verzeichnisbaum der App.
  • :folder-open: micky/
    • :folder-open: plugins/
      • :document: event_generator.rb
      • :document: gasthaus.rb
      • ...
    • :document: qt_app.rb
    • ...

Also schauen wir mal, wie wir alle Plugins in unsere App laden können. Wir benutzen dafür Rubys require Methode. Diese bekommt als Parameter einen Dateipfad und führt dann das im Dateipfad bezeichnete Script aus. Alle im ausgeführten Script definierten Klassen, Module und Konstanten sind anschließend im require-aufrufenden Script verfügbar.

Haben wir also das folgende Setup
  • :folder-open: /
    • :document: datei_A.rb
    • :document: datei_B.rb


  • Quoted from "datei_A.rb"

    Ruby Source code

    1
    2
    
    require( './datei_B.rb' )
    print( M )

    Quoted from "datei_B.rb"

    Ruby Source code

    1
    
    M = "Hallo\n"


Und führen jetzt datei_A aus, bekommen wir "Hallo" ausgegeben. Unser Hauptscript hat sich alles, was das andere Script definiert hat geschnappt und kann jetzt damit arbeiten. Genau das, was wir brauchen.

Das Plugin laden funktioniert dann ganz einfach:

Ruby Source code

1
2
3
4
5
6
# Get array of filenames
all_plugin_scripts = Dir.glob('./plugins/*.rb')
# Iterate over all filenames
all_plugin_scripts.each do |file|
  require file
end


Ich mag es ja, wenn Code übersichtlich und leicht verständlich ist und das jetzige minimalistische Pluginsystem ist echt nicht schwer zu verstehen.
Zum Abschluss wollen wir noch kurz reinspicken, wie Gasthaus.rb eigentlich im Moment aussieht. Ich schreibe extra "im Moment", weil gerade alles noch in der Entwicklung ist und sich noch einiges Ändern kann. Methoden können noch umbenannt werden oder es kommen neue hinzu. Mal schauen.

Spoiler: Code

Ruby Source code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
require_relative "./event_generator.rb"
#============================================================================
# ** Creates RPG::Event Instances
#----------------------------------------------------------------------------
#  Generates Inn-Events from boilerplates
#============================================================================
module Gasthaus
  #--------------------------------------------------------------------------
  # * Generator Infos
  #--------------------------------------------------------------------------
  GENERATOR_VERSION = "0.2.0"
  #--------------------------------------------------------------------------
  # * This Generator requires user input
  #--------------------------------------------------------------------------
  def self.use_dialog
    return true
  end
  #--------------------------------------------------------------------------
  # * Label for the Generator trigger
  #--------------------------------------------------------------------------
  def self.label
    "Gasthausbesitzerin"
  end
  #--------------------------------------------------------------------------
  # * Define Dialog widgets
  #--------------------------------------------------------------------------
  def self.dialog_options
    cost     = Event_Generator::Dialog_Option.new( :cost, "Preis", 10, {:range=>(0..999)} )
    currency = Event_Generator::Dialog_Option.new( :currency, "Wärung", "G" )
    return [cost, currency]
  end
  #--------------------------------------------------------------------------
  # * Create a new Inn owner Event
  #--------------------------------------------------------------------------
  def self.create_from_options( options )
    cost = options[:cost]
    currency_name = options[:currency]
 
    ev = RPG::Event.new(0,0)
    ev.name = "Besitzerin"
    ev.pages[0].graphic.character_name = "123-Civilian23"
    ev.pages[0].list = [
      RPG::EventCommand.new( 101, 0, ["Eine Nacht kostet euch #{cost}#{currency_name}. Möchtet ihr bleiben? \\G"] ),
      RPG::EventCommand.new( 102, 0, [["Ja", "Nein"], 2] ),
      RPG::EventCommand.new( 402, 0, [0, "Ja"] ),
      RPG::EventCommand.new( 111, 1, [7, cost, 0] ),
      RPG::EventCommand.new( 125, 2, [1, 0, cost] ),
      RPG::EventCommand.new( 223, 2, [Tone.new(-255,-255,-255), 10] ),
      RPG::EventCommand.new( 249, 2, [RPG::AudioFile.new("014-Inn01", 80, 100)] ),
      RPG::EventCommand.new( 106, 2, [80] ),
      RPG::EventCommand.new( 314, 2, [0] ),
      RPG::EventCommand.new( 223, 2, [Tone.new(0,0,0), 10] ),
      RPG::EventCommand.new( 0, 2, [] ),
      RPG::EventCommand.new( 411, 1, [] ),
      RPG::EventCommand.new( 101, 2, ["Ihr habt nicht genug Geld."] ),
      RPG::EventCommand.new( 0, 2, [] ),
      RPG::EventCommand.new( 412, 1, [] ),
      RPG::EventCommand.new( 0, 1, [] ),
      RPG::EventCommand.new( 402, 0, [1, "Nein"] ),
      RPG::EventCommand.new( 0, 1, [] ),
      RPG::EventCommand.new( 404, 0, [] ),
      RPG::EventCommand.new( 0, 0, [] )
    ]
    return ev
  end
end
#============================================================================
# << Add generator in the list of available generators
#============================================================================
Event_Generator::CHILDS << Gasthaus
highlight to read


Wir sehen: Plugins sind Rubymodule, die ganz bestimmte Methode implementieren. Die App schaut dann einfach im Event_Generator::CHILDS-Array nach, welche Module es so gibt und zeigt dann für jedes Plugin einen Knopf an. Wenn der App-User den Knopf drückt, kommt bei Bedarf erst noch ein Dialog und ansonsten generiert die App mit dem Plugin ein neues Event und kopiert dieses dann in die Zwischenablage.

Soweit der Blick hinter die Kulissen. Noch ein schönes Wochenende!

This article has been read 359 times.


Comments (2)

  • 2

    by Playm (Sunday, October 1st 2017, 11:38pm)

    Upps. D:
    Habe schnell das R ergänzt.

  • 1

    by Josey (Sunday, October 1st 2017, 1:05am)

    Wirklich interessant und diesmal hab ich sofort verstanden, worum es geht! XD
    Der Code sieht wie etwas aus, dass ich nach dem Vobild auch hinkriegen könnte! Ich bin sehr interessiert, wies weitergeht! : D

    "Wäung"-> Da fehlt ein R : P

Blog navigation

Next article

Eine App schreiben, die RPG Maker Projekte öffnen kann

by Playm (Sunday, October 8th 2017, 5:10pm)

Previous article

Mit Ruby eine GUI bauen

by Playm (Sunday, September 24th 2017, 8:00am)