1

Mittwoch, 17. Juli 2013, 18:45

[Sammelthread] RGSS1 Engine Performance Tweaks, copy&paste-ready

Als ich heute den empfehlenswerten Scientia-Artikel zum Aufbau und Verarbeitung von Events in RGSS1 inklusive Vergleich mit RGSS3 gelesen habe, ist mir mal wieder vor Augen geführt worden, wie dilettantisch die Engine des XP streckenweise geschrieben ist. Ob die Scripts des RGSS2/3 durchweg als guter Code bezeichnet werden können, sei mal so dahingestellt; Fakt ist allerdings, dass im Ace viele der Unzulänglichkeiten des RGSS1 verbessert oder behoben wurden.
Als besonders augenscheinliches Beispiel sei hier der teure case-Switch in Interpreter 2 mit sage und schreibe 97 Einträgen genannt, der im Ace auf ganze 2 Zeilen geschrumpft wurde, bei gleicher Funktionalität.

Wenn das also im Ace klappt, warum sollte man die Verbesserung dann nicht auch im XP nachträglich einbauen können? Gesagt, getan:
Spoiler: execute_command

Ruby Quellcode

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
  #--------------------------------------------------------------------------
  # * Event Command Execution
  #--------------------------------------------------------------------------
  def execute_command
    # If last to arrive for list of event commands
    if @index >= @list.size - 1
      # End event
      command_end
      # Continue
      return true
    end
    # Make event command parameters available for reference via @parameters
    @parameters = @list[@index].parameters
    # Branch by command code
    #========== Neuer Code ==========
    command_code = @list[@index].code
    method_name = "command_#{command_code}"
    if respond_to?(method_name)
      send(method_name)
    else
      return true
    end
    #========== Neuer Code Ende ==========
    # alter Code von Z. 23 (case @list[@index].code) bis Z. 218 auskommentiert
  end
zum Lesen den Text mit der Maus markieren

Im eventlastigen "Dragon's Tears" konnte ich allein durch diesen simplen Edit auf der ersten Map die durchschnittlichen fps von 28 auf 30 heben. Da der neue Code im Endeffekt nichts anderes tut als der alte, sollte es auch zu keinen Konflikten mit anderen Scripts kommen.

Dieser Thread

...soll als Sammelsurium für solche Tweaks und Hacks der Standard-Scripts des XP dienen. Voraussetzung ist, dass Funktionalität und Kompatibilität nicht oder nur marginal beeinträchtigt werden. Sollte letzteres der Fall sein, gib ein paar Hinweise darauf, wie man seine eigenen Scripts anzupassen hat.
Vergiss bitte nicht, dass es auch Leuten ohne einen blassen Schimmer von RGSS möglich sein sollte, die Snippets einzubauen. Deswegen präzise beschreiben, wo der Code hinkommt bzw. was er wo ersetzt.

Zusammen ließen sich bestimmt ein paar fps zusätzlich aus dem durchschnittlichen XP-Spiel rauskitzeln, die wie bei Dragon's Tears durchaus den Unterschied zwischen spielbar und nicht spielbar ausmachen können.

Ich freu mich auf eure Fundstücke!

Gruß :WinkeWinke:

:book: Index

Tweak
von User
Beschreibung
Potential
Plug'n'Play?
Interpreter#execute_command Terv beschleunigt Event-Verarbeitung
:star: :star: :star-empty:
    :success:
Interpreter#execute_command Der Imperator beschleunigt Event-Verarbeitung
:star: :star: :star:
    :success:
Interpreter#command_355 Der Imperator beschleunigt Call-Scripts enorm
:star: :star: :star:
    :exclamation:
Window_BattleStatus#refresh Irrlicht Entlastung in Kämpfen
:star: :star: :star-empty:
    :exclamation:
:success: Einfügen und vergessen | :exclamation: Post und [Anmerkungen] genau befolgen

:crown: Ranking

Spoiler
  1. Der Imperator [ 6 :star:]
  2. Irrlicht [ 2 :star:] & Terv [ 2 :star:]
zum Lesen den Text mit der Maus markieren
Bild

Dieser Beitrag wurde bereits 48 mal editiert, zuletzt von »Terv« (18. Juli 2013, 18:45)


Der Imperator

Oberschurke im Ruhestand

Motto: "Satzzeichen sind keine Rudeltiere." - Chesra

  • Nachricht senden

2

Mittwoch, 17. Juli 2013, 22:00

Spoiler: Veraltet. Bitte tiefer nachlesen!
Süßer Thread. Habe so gleich ein uraltes Test-Projekt von mir ausgegraben, in dem ich's ähnlich löste:
Spoiler: Interpreter#execute_command()

Ruby Quellcode

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
class Interpreter
  @@_VTBL = {
    :hack => :command_script_hack,
    101 => :command_101, 102 => :command_102, 103 => :command_103,
    104 => :command_104, 105 => :command_105, 106 => :command_106,
    111 => :command_111, 112 => :command_112, 113 => :command_113,
    115 => :command_115, 116 => :command_116, 117 => :command_117,
    118 => :command_118, 119 => :command_119, 121 => :command_121,
    122 => :command_122, 123 => :command_123, 124 => :command_124,
    125 => :command_125, 126 => :command_126, 127 => :command_127,
    128 => :command_128, 129 => :command_129, 131 => :command_131,
    132 => :command_132, 133 => :command_133, 134 => :command_134,
    135 => :command_135, 136 => :command_136, 201 => :command_201,
    202 => :command_202, 203 => :command_203, 204 => :command_204,
    205 => :command_205, 206 => :command_206, 207 => :command_207,
    208 => :command_208, 209 => :command_209, 210 => :command_210,
    221 => :command_221, 222 => :command_222, 223 => :command_223,
    224 => :command_224, 225 => :command_225, 231 => :command_231,
    232 => :command_232, 233 => :command_233, 234 => :command_234,
    235 => :command_235, 236 => :command_236, 241 => :command_241,
    242 => :command_242, 245 => :command_245, 246 => :command_246,
    247 => :command_247, 248 => :command_248, 249 => :command_249,
    250 => :command_250, 251 => :command_251, 301 => :command_301,
    302 => :command_302, 303 => :command_303, 311 => :command_311,
    312 => :command_312, 313 => :command_313, 314 => :command_314,
    315 => :command_315, 316 => :command_316, 317 => :command_317,
    318 => :command_318, 319 => :command_319, 320 => :command_320,
    321 => :command_321, 322 => :command_322, 331 => :command_331,
    332 => :command_332, 333 => :command_333, 334 => :command_334,
    335 => :command_335, 336 => :command_336, 337 => :command_337,
    338 => :command_338, 339 => :command_339, 340 => :command_340,
    351 => :command_351, 352 => :command_352, 353 => :command_353,
    354 => :command_354, 355 => :command_355, 402 => :command_402,
    403 => :command_403, 411 => :command_411, 413 => :command_413,
    601 => :command_601, 602 => :command_602, 603 => :command_603,
  }
  def execute_command()
    if @index >= @list.size-1 then
      command_end()
      true
    else
      l = @list[@index]
      @parameters = l.parameters
      sym = @@_VTBL[l.code]
      sym ? send(sym) : true
    end
  end
end
zum Lesen den Text mit der Maus markieren
@@_VTBL dient hier dazu, dass Symbols nur einmalig generiert werden. Methoden werden in Ruby mit Hilfe von Symbols gelistet, die lediglich wenige Bits plus ein paar BitFlags sind. Sie fressen ebenso wenig Speicher wie Integer und sind ebenso schnell miteinander verglichen. Methoden-Suche über Strings generiert Symbole, die wiederum Hashes von zugehörigen Strings sind. (Wahrscheinlich CRC-16 oder sowas...) Diese zahllosen Schritte von Integer=>String, String+String, und String=>Symbol bleiben somit erspart und der Methodenaufruf ist nochmals schneller.

Wer sich wegen des Hacks wundert:
Spoiler: Interpreter#command_355()

Ruby Quellcode

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
$EVY_EVIL_HACK = {}
class Interpreter
  def command_355()
    i = @index+1
    script = @list[@index].parameters[0]+"\n"
    while @list[i].code==655 do
      script += @list[i].parameters[0]+"\n"
      @list.delete_at(i)
    end
    h = script.hash
    @list[@index].code = :hack
    @list[@index].parameters = [h,script]
    unless $EVY_EVIL_HACK[h] then
      # Begin hack of doom!
      p = eval("proc{"+script+"}")
      $EVY_EVIL_HACK[h] = p
      # End hack of doom!
      p.call()&&true||false
    else
      $EVY_EVIL_HACK[h].call()&&true||false
    end
  end
  def command_script_hack()
    h = @list[@index].parameters[0]
    unless $EVY_EVIL_HACK[h] then
      $EVY_EVIL_HACK[h] = eval("proc{"+@list[@index].parameters[1]+"}")
    end
    $EVY_EVIL_HACK[h].call()&&true||false
  end
end
zum Lesen den Text mit der Maus markieren
Dieses Teil beschleunigt Call-Scripts enorm, indem Kernel::eval(...) nur einmalig ausgeführt wird. Event-Codes werden hierbei überschrieben. Der Umweg über die globale Variable verhindert, dass RGSS beim Speichern explodiert.

Beide Scripts einfach unter Interpreter 7.
Viel Spaß damit.

Edit Den Hack gehacked.
Edit Den Hack entkäfert.
zum Lesen den Text mit der Maus markieren
Edit Dieser Post ist etwas veraltet. Bitte diesen und den folgenden von mir überspringen.
  • :medal: Werbung

    Bild

    Cpp Quellcode

    1
    
    #define TRUE FALSE //Happy debugging suckers
    (Einfach nur wundervoll.)
  • :palette: 1plus3 :cake:

    Bild
  • :fires: Nuuuhminaaah

    Bild
  • :medal: compétences

    mes compétences
    :heart_full: :heart_full: :heart_full: :heart_full: :heart_full: max.
    :ruler-triangle: Maps machen :heart_full: :heart-empty: :heart-empty: :heart-empty: :heart-empty:
    :media-player: Musik machen :heart_full: :heart-half: :heart-empty: :heart-empty: :heart-empty:
    :cup: Scripts machen :heart_full: :heart_full: :heart_full: :heart_full: :heart-break:
    :paper: Story ausdenken :heart_full: :heart_full: :heart_full: :heart-empty: :heart-empty:
    :cut: Pixeln und so :heart-empty: :heart-empty: :heart-empty: :heart-empty: :heart-empty:
    :game: Events proggen :heart_full: :heart_full: :heart_full: :heart_full: :heart_full:
    (Dieser Tab ist rein satirisch.)
  • :folder-open: mes projets

    • :addressbook: Silentium
      :book: Name: Silentium
      :rmxp: Maker: Eigenbau (C++, x86-SSE/AVX-Assembly, Ruby/Lua)

      :paper: Story
      :game: NPCs
      :cup: Scripts
      :drill: Ressis
      :ruler-triangle: Maps
      :compile: Gesamt
      (3+4)% 42 69% 0815 -17.438 103.38% ± 6.3mm²

      (Die Tabelle erfüllt lediglich satirische Zwecke.)
    • :compile: Onyx
      Eine in C++ implementierte, modulare, plattformunabhängige, virtuelle Maschine. Die Test-Version ist bereits halb fertig. Ab dann gibt es vielleicht mehr Infos. Sie soll die auf dem ersten Blick LISP-artige und eigens dafür konstruierte Sprache Obsidian ausführen können. Experimentell wird auch ein Lua-Compiler für Onyx gebaut. Ziel ist eine leistungsfähige, virtuelle Maschine für beliebige Scriptsprachen. Theoretisch gesehen müsste man bloß noch einen kompatiblen Compiler schreiben, der Quellcode jener Sprache in Onyx-Assembly, oder direkt in Onyx-Bytecode übersetzt. Ob die jemand nutzen wird, ist eine andere Frage und nur ein sekundäres... nein, eher tertiäres Ziel dieser VM. Primär dient es mir lediglich dazu, mein Verständnis von Hardware, ISA, und Assembly zu vertiefen, sowie eigene Grenzen auszutesten.

      :exclamation: Warnung!
      Das Entwickeln einer virtuellen Maschine oder Programmiersprache (im wahnsinnigsten Fall beides) ist eine höchst komplizierte Tätigkeit, aus der viel Frust und Hirnmatsche hervor gehen. Sollte sich dennoch ein ähnlich wahnsinniger finden, der sowas zusammen schustern will, so lege ich ihm/ihr die folgenden Bücher ans Herz:
      • Compiler - Das Drachenbuch [978-3-8273-7097-6]
        Dieses Buch schlachtet ausführlich und leicht verständlich die Grundlagen bis hoch zu den Experten-Techniken des Compilerbaus aus. Es fängt mit der Automaten-Theorie und formalen Sprachen an, arbeitet sich durch Analysetechniken vor, und landet schließlich bei Techniken wie Optimierung und Register-Zuweisung. Das Buch wiegt 3Kg oder 4Kg. Hab's mal gewogen. Ist also nicht gerade die Lektüre für unterwegs.

      • Computerarchitektur [3-8273-7016-7]
        Hier werden leicht verständlich die wichtigsten Entwicklungen der Rechnerarchitekturen erklärt (Gut, das Buch ist in die Jahre gekommen, aber der Weg zu heute ist ein winziger Schritt, den man sich nach diesem Buch selbst erdenken kann). Hauptbestandteil des Buchs ist eine relativ umfassende Betrachtung der Funktionsweise dreier gänzlich unterschiedlicher, aber dominierender Prozessor-Typen am Beispiel des Pentium II, UltraSPARC II, sowie picoJava. Die meisten Elemente dieses Buchs sind zwar für die Konstruktion einer virtuellen Maschine irrelevant, oder aufgrund der Tatsache, dass die VM Software ist und z.B. Byte-Grenzen hat, sogar zu Leistungseinbußen führen kann, doch ist ein hinreichendes Verständnis dieser Maschinen, mit denen wir arbeiten, äußerst hilfreich für die Überlegungen, wie die VM arbeiten soll.

      Es kann sehr hilfreich und inspirierend sein, den Code quelloffener, virtueller Maschinen anderer Sprachen zu überfliegen. Meine Lieblings-Quelle war und ist stets die VM von Lua. Sie ist schlank, verständlich, in C implementiert, und basiert im Gegensatz zu vielen anderen Scriptsprachen-VMs auf einer Register-Maschine statt einer Stapelmaschine. Es wäre natürlich vorteilhaft, die entsprechende Sprache zu verstehen, in der man auch die eigene VM implementieren will. Weiterhin ist es äußerst vorteilhaft, eine leistungsstarke und bequeme Sprache wie C++ zu beherrschen, um die VM zu implementieren. Und bevor irgendwer auf die Idee kommt: Assembly ist NICHT als dominierende Sprache für den Bau einer VM geeignet. Wer die Frage des "Warum?" nicht beantworten kann, sollte zunächst die gewählte Sprache und Assembly hinreichend verstehen lernen, und es dann erneut mit der Frage versuchen. Es lohnt sich dennoch, Assembly zu lernen. Allein schon, um erneut das Verständnis zu vertiefen, zumal ihr mehr oder weniger gezwungen seid, auch für eure VM eine Assembler-Sprache zu entwickeln (Außer natürlich ihr schreibt eure Test-Programme Bit für Bit ;3).
  • :locale: enfin

    Je ne peux pas parler français.
    C'est tout ce que Goodle et les restes de cours de français.
Signaturstand: 24.07.2013

Irrlicht

Leuchtendes Irgendwas

Motto: Keep shining!

  • Nachricht senden

3

Donnerstag, 18. Juli 2013, 00:04

Zum Thema Interpreter:

Ich hatte das ganze irgendwann mal ausprobiert und hatte für mich auch mit künstlich gestreckten Eventlisten keine Verbesserung bemerkt - allerdings bestanden meine Events auch zu 90% aus "Control Variables", "Conditional Branch" oder "Call Common Event", die bis auf das Leerzeilenkommando/"Branch End" relativ weit oben in der Case-Anweisung stehen.
Ob es bei großen Events einen Frame raushaut oder nicht kann ich allerdings nicht genau sagen, bei mir schwanken die FPS bei "kritischen" XP-Spielen ohnehin immer etwas.
Wie auch immer es sei, hüscher sieht die Lösung natürlich allemal aus. :)

Zu Everys Hack:
Die globale Variable hat aber den Nachteil dass man in den Scriptbefehlen nicht mehr auf die Instanzattribute des Interpreters zugreifen sollte. Kommdos wie
@wait_count = $game_variables[5] <- variables "Wait"
@event_id = $game_variables[5] <- Bezug von "this event" ändern
oder
@params = [$game_variables[5]]; command_117 <- variables "Call Common Event"
funktionieren in parallelen Prozessen oder nach Laden eines zweiten Spielstandes unter Umständen nicht mehr (anderes Interpreter-Objekt).

Ansonsten: auch wenn String#hash ein paar Bytes spart und die Chancen einer Kollision mehr als gering sind - wer soll den Fehler finden falls es doch einmal zu einer Kollision kommt? :-/


Aber um auch mal etwas "nicht-meckriges" zum Thema beizutragen:

Wer einmal im Kampf ein Event gestartet hat und dabei einen starken Leistungseinbruch bemerkt hat:
Das dürfte daran liegen dass Scene_Battle während der Ausführung des Events nicht merkt ob sich die HP/MP/Name eines Actors oder die Zusammenstellung der Party geändert hat und deshalb vorsichtshalber jeden Frame das gesammte Kampfstatus-Fenster neu zeichnen lässt.

Einfacher Workaround: Den Zustand aller im Fenster gezeichneten Informationen festhalten und "refresh" nur zulassen wenn sich mindestens eine Information tatsächlich geändert hat:
Spoiler

Ruby Quellcode

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
class Window_BattleStatus < Window_Base
 
  #--------------------------------------------------------------------------
  # * Refresh
  #--------------------------------------------------------------------------
  alias_method(:refresh_ILC_check_for_changes, :refresh)
  def refresh
    refresh_ILC_check_for_changes  if update_item_status
  end
 
  #--------------------------------------------------------------------------
  # * Check if refreshing is reasonable:
  #--------------------------------------------------------------------------
  def update_item_status
    new_items = 
    $game_party.actors.collect do |actor|
      next [actor.name, actor.hp, actor.sp, actor.maxhp, actor.maxsp,
      make_battler_state_text(actor, 120, true)]
    end
    if new_items != @current_item_status
      @current_item_status = new_items
      return true
    end
    return false
  end
end
zum Lesen den Text mit der Maus markieren


Anmerkung: Der Workaround orientiert sich stark am Standardkampfsystem. Das oben beschriebene Problem betrifft aber auch einige weitere Kampfsysteme die auf dem Standard aufbauen.
Im Kampfstatusfenster werden im Standard nur Name, HP, SP und Statuseffekt jedes Actors gespeichert und nur diese Werte werden im Workaround überprüft. Bei anderen Werten müssten die Zeilen 17-18 im Workaround angepasst werden. Daher ist auch dieses Script nur mit Vorsicht genießen. :)

Gruß
Irrlicht

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »Irrlicht« (18. Juli 2013, 00:32)


Der Imperator

Oberschurke im Ruhestand

Motto: "Satzzeichen sind keine Rudeltiere." - Chesra

  • Nachricht senden

4

Donnerstag, 18. Juli 2013, 12:06

Spoiler: Veraltet. Bitte weiter unten nachlesen.

Zitat

Die globale Variable hat aber den Nachteil dass man in den Scriptbefehlen nicht mehr auf die Instanzattribute des Interpreters zugreifen sollte.

Ruby Quellcode

1
$game_system.map_interpreter.instance_variable_get(:@wait_count)
Gibt für alles einen Workaround.

Zitat

... funktionieren in parallelen Prozessen oder nach Laden eines zweiten Spielstandes unter Umständen nicht mehr (anderes Interpreter-Objekt)
Mit dem Workaround ein nicht-existentes Problem.

Zitat

Ansonsten: auch wenn String#hash ein paar Bytes spart und die Chancen einer Kollision mehr als gering sind - wer soll den Fehler finden falls es doch einmal zu einer Kollision kommt? :-/
Das ist kein Problem meines Hacks, sondern ein Problem der Ruby-Implementierung.

Süßer Workaround für das KS. Schön wäre noch ein Hack, der die Ultra-Lags von In-Battle-Messages behebt.

@Terv:
Schöne Tabelle!

Edit

Quellcode

1
2
3
4
5
6
7
8
irb(main):001:0> def foo()
irb(main):002:1>  x=4
irb(main):003:1>  p=eval("proc{y=x;return y}")
irb(main):004:1>  p.call()
irb(main):005:1> end
=> nil
irb(main):006:0> foo()
=> 4
Ein anderer möglicher Workaround wäre somit:

Ruby Quellcode

1
p = eval("proc{this=self;"+script+"}")
... mit "this" als Interpreter. Aber da besteht dann das Problem des eventuell gewechselten Interpreters.
Edit Habe einen Bug im Call-Script-Hack gefunden und so gleich behoben. Im Übrigen sind besagte Workarounds nicht von Nöten. Habe es mit einer simplen "Hello, World!"-Funktion im Interpreter getestet. Mapwechsel und das Laden von Spielständen machen da nichts aus. Der Code funktioniert und kann auf Internes zugreifen. Höchstens Kämpfe könnten da noch was ausmachen. Freiwillige vor.

Zitat

Die globale Variable hat aber den Nachteil dass [...]
Nö. Wo die Proc gespeichert wird ist wumpe. Die Proc wird zum Closure und nimmt sich alles vom Interpreter mit nach Walhall.
zum Lesen den Text mit der Maus markieren
Edit Anregung für VX Ace -Nutzer:

Quellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
irb(main):001:0> def foo()
irb(main):002:1>  x=4
irb(main):003:1>   p=RubyVM::InstructionSequence::compile("y=x;y")
irb(main):004:1>   p.eval()
irb(main):005:1> end
=> nil
irb(main):006:0> foo()
NameError: undefined local variable or method `x' for main:Object
        from <compiled>:1:in `<compiled>'
        from (irb):4:in `eval'
        from (irb):4:in `foo'
        from (irb):6
        from C:/EVREY/Sprachen/Ruby/Ruby200x64/bin/irb:12:in `<main>'
Die InstructionSequences aus Ruby 1.9 sind nochmals ein gutes Stück flotter als Procs und Lambdas, bieten allerdings keine Closure-Mechanismen. Da muss der Workaround über $game_system her. Wer sich für diese Mikro-Optimierung interessiert, mag es austesten.
Edit Hinweis:
InstructionSequences können keine Parameter annehmen. Sie stellen abgeschotteten Top-Level-Ruby-Code dar. Um mit ihnen an den aktuellen Interpreter zu kommen, braucht es eine weitere Hack-Funktion:

Ruby Quellcode

1
2
3
4
5
class Interpreter
  def self::current()
    @@__THIS__
  end
end
Nun erhält man den aktuellen Interpreter nicht über #this(), sondern über Interpreter::current().
  • :medal: Werbung

    Bild

    Cpp Quellcode

    1
    
    #define TRUE FALSE //Happy debugging suckers
    (Einfach nur wundervoll.)
  • :palette: 1plus3 :cake:

    Bild
  • :fires: Nuuuhminaaah

    Bild
  • :medal: compétences

    mes compétences
    :heart_full: :heart_full: :heart_full: :heart_full: :heart_full: max.
    :ruler-triangle: Maps machen :heart_full: :heart-empty: :heart-empty: :heart-empty: :heart-empty:
    :media-player: Musik machen :heart_full: :heart-half: :heart-empty: :heart-empty: :heart-empty:
    :cup: Scripts machen :heart_full: :heart_full: :heart_full: :heart_full: :heart-break:
    :paper: Story ausdenken :heart_full: :heart_full: :heart_full: :heart-empty: :heart-empty:
    :cut: Pixeln und so :heart-empty: :heart-empty: :heart-empty: :heart-empty: :heart-empty:
    :game: Events proggen :heart_full: :heart_full: :heart_full: :heart_full: :heart_full:
    (Dieser Tab ist rein satirisch.)
  • :folder-open: mes projets

    • :addressbook: Silentium
      :book: Name: Silentium
      :rmxp: Maker: Eigenbau (C++, x86-SSE/AVX-Assembly, Ruby/Lua)

      :paper: Story
      :game: NPCs
      :cup: Scripts
      :drill: Ressis
      :ruler-triangle: Maps
      :compile: Gesamt
      (3+4)% 42 69% 0815 -17.438 103.38% ± 6.3mm²

      (Die Tabelle erfüllt lediglich satirische Zwecke.)
    • :compile: Onyx
      Eine in C++ implementierte, modulare, plattformunabhängige, virtuelle Maschine. Die Test-Version ist bereits halb fertig. Ab dann gibt es vielleicht mehr Infos. Sie soll die auf dem ersten Blick LISP-artige und eigens dafür konstruierte Sprache Obsidian ausführen können. Experimentell wird auch ein Lua-Compiler für Onyx gebaut. Ziel ist eine leistungsfähige, virtuelle Maschine für beliebige Scriptsprachen. Theoretisch gesehen müsste man bloß noch einen kompatiblen Compiler schreiben, der Quellcode jener Sprache in Onyx-Assembly, oder direkt in Onyx-Bytecode übersetzt. Ob die jemand nutzen wird, ist eine andere Frage und nur ein sekundäres... nein, eher tertiäres Ziel dieser VM. Primär dient es mir lediglich dazu, mein Verständnis von Hardware, ISA, und Assembly zu vertiefen, sowie eigene Grenzen auszutesten.

      :exclamation: Warnung!
      Das Entwickeln einer virtuellen Maschine oder Programmiersprache (im wahnsinnigsten Fall beides) ist eine höchst komplizierte Tätigkeit, aus der viel Frust und Hirnmatsche hervor gehen. Sollte sich dennoch ein ähnlich wahnsinniger finden, der sowas zusammen schustern will, so lege ich ihm/ihr die folgenden Bücher ans Herz:
      • Compiler - Das Drachenbuch [978-3-8273-7097-6]
        Dieses Buch schlachtet ausführlich und leicht verständlich die Grundlagen bis hoch zu den Experten-Techniken des Compilerbaus aus. Es fängt mit der Automaten-Theorie und formalen Sprachen an, arbeitet sich durch Analysetechniken vor, und landet schließlich bei Techniken wie Optimierung und Register-Zuweisung. Das Buch wiegt 3Kg oder 4Kg. Hab's mal gewogen. Ist also nicht gerade die Lektüre für unterwegs.

      • Computerarchitektur [3-8273-7016-7]
        Hier werden leicht verständlich die wichtigsten Entwicklungen der Rechnerarchitekturen erklärt (Gut, das Buch ist in die Jahre gekommen, aber der Weg zu heute ist ein winziger Schritt, den man sich nach diesem Buch selbst erdenken kann). Hauptbestandteil des Buchs ist eine relativ umfassende Betrachtung der Funktionsweise dreier gänzlich unterschiedlicher, aber dominierender Prozessor-Typen am Beispiel des Pentium II, UltraSPARC II, sowie picoJava. Die meisten Elemente dieses Buchs sind zwar für die Konstruktion einer virtuellen Maschine irrelevant, oder aufgrund der Tatsache, dass die VM Software ist und z.B. Byte-Grenzen hat, sogar zu Leistungseinbußen führen kann, doch ist ein hinreichendes Verständnis dieser Maschinen, mit denen wir arbeiten, äußerst hilfreich für die Überlegungen, wie die VM arbeiten soll.

      Es kann sehr hilfreich und inspirierend sein, den Code quelloffener, virtueller Maschinen anderer Sprachen zu überfliegen. Meine Lieblings-Quelle war und ist stets die VM von Lua. Sie ist schlank, verständlich, in C implementiert, und basiert im Gegensatz zu vielen anderen Scriptsprachen-VMs auf einer Register-Maschine statt einer Stapelmaschine. Es wäre natürlich vorteilhaft, die entsprechende Sprache zu verstehen, in der man auch die eigene VM implementieren will. Weiterhin ist es äußerst vorteilhaft, eine leistungsstarke und bequeme Sprache wie C++ zu beherrschen, um die VM zu implementieren. Und bevor irgendwer auf die Idee kommt: Assembly ist NICHT als dominierende Sprache für den Bau einer VM geeignet. Wer die Frage des "Warum?" nicht beantworten kann, sollte zunächst die gewählte Sprache und Assembly hinreichend verstehen lernen, und es dann erneut mit der Frage versuchen. Es lohnt sich dennoch, Assembly zu lernen. Allein schon, um erneut das Verständnis zu vertiefen, zumal ihr mehr oder weniger gezwungen seid, auch für eure VM eine Assembler-Sprache zu entwickeln (Außer natürlich ihr schreibt eure Test-Programme Bit für Bit ;3).
  • :locale: enfin

    Je ne peux pas parler français.
    C'est tout ce que Goodle et les restes de cours de français.
Signaturstand: 24.07.2013

Irrlicht

Leuchtendes Irgendwas

Motto: Keep shining!

  • Nachricht senden

5

Donnerstag, 18. Juli 2013, 13:09

Zitat von »Der Imperator«

Die Proc wird zum Closure [...]

Und genau deshalb funktioniert das folgende Beispiel nicht:
Man erstelle 3 Events mit "Parallel Process" und folgendem Code:
@>Script: @wait_count += 100
@>Erase Event
@>
Event 2 und 3 verschwinden nun sofort, Event 1 nach 300 Frames. Startet man nochmal ein neues Spiel verschwinden alle 3 Events sofort (Alle Aufrufe wirken sich auf das selbe Interpreter-Objekt aus).

Der Abschnitt über RubyVM::InstructionSequence klingt aber sehr interressant, werde ich mal mit testen, danke. :)

Ansonsten sollten sich die Lags bei In-Battle-Messages mit dem Script aus dem letzten Post verringern lassen.

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »Irrlicht« (18. Juli 2013, 14:34)


Der Imperator

Oberschurke im Ruhestand

Motto: "Satzzeichen sind keine Rudeltiere." - Chesra

  • Nachricht senden

6

Donnerstag, 18. Juli 2013, 17:32

:ruby: Hack, der Call-Scripts mit Lichtgeschwindigkeit durch den Prozessor jagt!


Diese zwei Scripts gehören unter die "Interpreter"-Scripts aus RGSS und irgendwo über Main. Kompatiblität ist weitesgehend bewahrt. Bitte die Hinweise zur Änderung von Call-Scripts weiter unten in diesem Post beachten! Ist absolut nicht viel zu ändern. Simple Kopier-Pasta. Der Code funktioniert sehr simpel: Event-Codes werden dazu genutzt, die aufzurufende Funktion in einer Look-Up-Table nachzuschlagen. Eine mögliche Verbesserung ist in Arbeit. Die Call-Scripts werden zu aufrufbare Procs kompilliert, wodurch das extrem langsame eval() deutlich seltener ausgeführt werden muss. Der Hack erkennt identische Call-Scripts, so dass diese Ersparnis Event- und Map-übergreifend ist. Ein wirklicher Geschwindigkeits-Gewinn zeigt sich erst, wenn viele Call-Scripts mehrmals ausgeführt werden. Einmalige Call-Scripts erleiden keine bemerkbaren Geschwindigkeits-Verluste.

:exclamation: Hinweis:
Besagte Änderungen betreffen _nicht_ alle Call-Scripts! Nur jene, die direkt auf Member des Interpreters zugreifen. Closures zu Interpretern werden in dieser Version verhindert. Das Script führt nun zu einem NoMethodError, sollte man versuchen, ohne die unten stehenden Umwege auf Methoden des Interpreters zuzugreifen. Zusätzlich kommt im Debug-Modus ein RuntimeError, wenn man ohne diese Umwege auf Instanz-Variablen zugreift. So entdeckt man relativ schnell, welche Call-Scripts Änderungen bedürfen und welche nicht.

:compile: Zwei Hacks für den Interpreter


Spoiler: Interpreter#execute_command() --- Schnellere Auswahl der Event-Codes

Ruby Quellcode

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
class Interpreter
  @@_VTBL = {
    :hack => :command_script_hack,
    101 => :command_101, 102 => :command_102, 103 => :command_103,
    104 => :command_104, 105 => :command_105, 106 => :command_106,
    111 => :command_111, 112 => :command_112, 113 => :command_113,
    115 => :command_115, 116 => :command_116, 117 => :command_117,
    118 => :command_118, 119 => :command_119, 121 => :command_121,
    122 => :command_122, 123 => :command_123, 124 => :command_124,
    125 => :command_125, 126 => :command_126, 127 => :command_127,
    128 => :command_128, 129 => :command_129, 131 => :command_131,
    132 => :command_132, 133 => :command_133, 134 => :command_134,
    135 => :command_135, 136 => :command_136, 201 => :command_201,
    202 => :command_202, 203 => :command_203, 204 => :command_204,
    205 => :command_205, 206 => :command_206, 207 => :command_207,
    208 => :command_208, 209 => :command_209, 210 => :command_210,
    221 => :command_221, 222 => :command_222, 223 => :command_223,
    224 => :command_224, 225 => :command_225, 231 => :command_231,
    232 => :command_232, 233 => :command_233, 234 => :command_234,
    235 => :command_235, 236 => :command_236, 241 => :command_241,
    242 => :command_242, 245 => :command_245, 246 => :command_246,
    247 => :command_247, 248 => :command_248, 249 => :command_249,
    250 => :command_250, 251 => :command_251, 301 => :command_301,
    302 => :command_302, 303 => :command_303, 311 => :command_311,
    312 => :command_312, 313 => :command_313, 314 => :command_314,
    315 => :command_315, 316 => :command_316, 317 => :command_317,
    318 => :command_318, 319 => :command_319, 320 => :command_320,
    321 => :command_321, 322 => :command_322, 331 => :command_331,
    332 => :command_332, 333 => :command_333, 334 => :command_334,
    335 => :command_335, 336 => :command_336, 337 => :command_337,
    338 => :command_338, 339 => :command_339, 340 => :command_340,
    351 => :command_351, 352 => :command_352, 353 => :command_353,
    354 => :command_354, 355 => :command_355, 402 => :command_402,
    403 => :command_403, 411 => :command_411, 413 => :command_413,
    601 => :command_601, 602 => :command_602, 603 => :command_603,
  }
  def execute_command()
    if @index >= @list.size-1 then
      command_end()
      true
    else
      l = @list[@index]
      @parameters = l.parameters
      sym = @@_VTBL[l.code]
      sym ? send(sym) : true
    end
  end
end
zum Lesen den Text mit der Maus markieren
Spoiler: Interpreter#command_355() --- Stark beschleunigte Call-Scripts

Ruby Quellcode

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
def this_interpreter()
  Interpreter::current()
end
 
$EVY_EVIL_HACK = {}
class Interpreter
  @@__THIS__ = nil
  attr_accessor(:depth,:main,:map_id,:event_id,:message_waiting)
  attr_accessor(:move_route_waiting,:button_input_variable_id,:loop_count)
  attr_accessor(:wait_count,:child_interpreter,:branch,:list,:index)
  alias old_update_3e66d02b update
  def update()
    tmp = @@__THIS__ # Für den unwahrscheinlichen Fall, dass irgendein Held eine Interpreterception auslöst.
    @@__THIS__ = self
    old_update_3e66d02b()
    @@__THIS__ = tmp
  end
  def self::current()
    @@__THIS__
  end
  def command_355()
    i = @index+1
    script = @list[@index].parameters[0]+"\n"
    while @list[i].code==655 do
      script += @list[i].parameters[0]+"\n"
      @list.delete_at(i)
    end
    h = script.hash
    @list[@index].code = :hack
    @list[@index].parameters = [h,script]
    unless $EVY_EVIL_HACK[h] then
      # Begin hack of doom!
      p = eval("proc{#{script}}",TOPLEVEL_BINDING)
      $EVY_EVIL_HACK[h] = p
      # End hack of doom!
      p.call()&&true||false
    else
      $EVY_EVIL_HACK[h].call()&&true||false
    end
  end
  def command_script_hack()
    h = @list[@index].parameters[0]
    unless $EVY_EVIL_HACK[h] then
      $EVY_EVIL_HACK[h] = eval("proc{#{@list[@index].parameters[1]}}",TOPLEVEL_BINDING)
    end
    $EVY_EVIL_HACK[h].call()&&true||false
  end
end
zum Lesen den Text mit der Maus markieren
Das untere Script braucht das obere, um zu funktionieren, da dort der neue Event-Code ":hack" enthalten ist und verarbeitet wird. Man sollte mit diesem Script nicht mehr direkt auf Instanzvariablen wie z.B. @wait_count oder Methoden wie z.B. #running?() zugreifen. Statt dessen ist ein Umweg über die Funktion ::this_interpreter() von Nöten. ::this_interpreter() referenziert _immer_ den aktuell laufenden Interpreter oder nil (nil ist für Call-Scripts allerdings irrelevant und nicht existent). Ein Beispiel, wie Call-Scripts zu verändern sind:

Ruby Quellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# ... vorher:
@wait_count += 100
$test = running?
true
 
# ... nachher:
this_interpreter.wait_count += 100
$test = this_interpreter.running?
true
 
# ... oder auch:
Interpreter::current.wait_count += 100
$test = Interpreter::current.running?
true


Spoiler: Nicht allzu spannend.
Edit Habe mal das Interpreter::current() verbaut.
Edit Die experimentierfreudigen Hacker müssen einfach aus dem Code hier...

Ruby Quellcode

1
2
3
p=eval("proc{"+script+"}")
#...
p.call()
... sowas hier machen (Funktioniert nur mit Ruby 1.9+, also RGSS3):

Ruby Quellcode

1
2
3
p=RubyVM::InstructionSequence::compile(script)
#...
p.eval() # <== #eval(), nicht #call() !


Experimentelle Mini-Verbesserung

Es hat sich nicht allzu viel verändert. Die numerischen Event-Codes werden einfach direkt durch die Symbole ersetzt. Es ist daher möglich, dass Scripts, die direkt an Event-Codes fummeln, Probleme bekämen. Zum Beispiel solche, die Event-Kommentare auslesen wollen. Dürfte für sich wiederholende Events einen kleinen Boost geben, sonst ist der Effekt allerdings wohl kaum nennenswert.
Spoiler

Ruby Quellcode

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
class Interpreter
  @@_VTBL = {
    101 => :command_101, 102 => :command_102, 103 => :command_103,
    104 => :command_104, 105 => :command_105, 106 => :command_106,
    111 => :command_111, 112 => :command_112, 113 => :command_113,
    115 => :command_115, 116 => :command_116, 117 => :command_117,
    118 => :command_118, 119 => :command_119, 121 => :command_121,
    122 => :command_122, 123 => :command_123, 124 => :command_124,
    125 => :command_125, 126 => :command_126, 127 => :command_127,
    128 => :command_128, 129 => :command_129, 131 => :command_131,
    132 => :command_132, 133 => :command_133, 134 => :command_134,
    135 => :command_135, 136 => :command_136, 201 => :command_201,
    202 => :command_202, 203 => :command_203, 204 => :command_204,
    205 => :command_205, 206 => :command_206, 207 => :command_207,
    208 => :command_208, 209 => :command_209, 210 => :command_210,
    221 => :command_221, 222 => :command_222, 223 => :command_223,
    224 => :command_224, 225 => :command_225, 231 => :command_231,
    232 => :command_232, 233 => :command_233, 234 => :command_234,
    235 => :command_235, 236 => :command_236, 241 => :command_241,
    242 => :command_242, 245 => :command_245, 246 => :command_246,
    247 => :command_247, 248 => :command_248, 249 => :command_249,
    250 => :command_250, 251 => :command_251, 301 => :command_301,
    302 => :command_302, 303 => :command_303, 311 => :command_311,
    312 => :command_312, 313 => :command_313, 314 => :command_314,
    315 => :command_315, 316 => :command_316, 317 => :command_317,
    318 => :command_318, 319 => :command_319, 320 => :command_320,
    321 => :command_321, 322 => :command_322, 331 => :command_331,
    332 => :command_332, 333 => :command_333, 334 => :command_334,
    335 => :command_335, 336 => :command_336, 337 => :command_337,
    338 => :command_338, 339 => :command_339, 340 => :command_340,
    351 => :command_351, 352 => :command_352, 353 => :command_353,
    354 => :command_354, 355 => :command_355, 402 => :command_402,
    403 => :command_403, 411 => :command_411, 413 => :command_413,
    601 => :command_601, 602 => :command_602, 603 => :command_603,
  }
  def execute_command()
    if @index >= @list.size-1 then
      command_end()
      true
    else
      l = @list[@index]
      @parameters = l.parameters
      if l.code.is_a? Integer then
        sym = @@_VTBL[l.code]
        l.code = sym
        sym ? send(sym) : true
      else
        send(l.code)
      end
    end
  end
end

Ruby Quellcode

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
$EVY_EVIL_HACK = {}
class Interpreter
  attr_accessor(:depth,:main,:map_id,:event_id,:message_waiting)
  attr_accessor(:move_route_waiting,:button_input_variable_id,:loop_count)
  attr_accessor(:wait_count,:child_interpreter,:branch,:list,:index)
  alias old_update_3e66d02b update
  def update()
    @@__THIS__ = self
    old_update_3e66d02b()
    @@__THIS__ = nil
  end
  def this()
    @@__THIS__
  end
  def self::current()
    @@__THIS__
  end
  def command_355()
    i = @index+1
    script = @list[@index].parameters[0]+"\n"
    while @list[i].code==655 do
      script += @list[i].parameters[0]+"\n"
      @list.delete_at(i)
    end
    h = script.hash
    @list[@index].code = :command_script_hack
    @list[@index].parameters = [h,script]
    unless $EVY_EVIL_HACK[h] then
      # Begin hack of doom!
      p = eval("proc{"+script+"}")
      $EVY_EVIL_HACK[h] = p
      # End hack of doom!
      p.call()&&true||false
    else
      $EVY_EVIL_HACK[h].call()&&true||false
    end
  end
  def command_script_hack()
    h = @list[@index].parameters[0]
    unless $EVY_EVIL_HACK[h] then
      $EVY_EVIL_HACK[h] = eval("proc{"+@list[@index].parameters[1]+"}")
    end
    $EVY_EVIL_HACK[h].call()&&true||false
  end
end
zum Lesen den Text mit der Maus markieren
zum Lesen den Text mit der Maus markieren
  • :medal: Werbung

    Bild

    Cpp Quellcode

    1
    
    #define TRUE FALSE //Happy debugging suckers
    (Einfach nur wundervoll.)
  • :palette: 1plus3 :cake:

    Bild
  • :fires: Nuuuhminaaah

    Bild
  • :medal: compétences

    mes compétences
    :heart_full: :heart_full: :heart_full: :heart_full: :heart_full: max.
    :ruler-triangle: Maps machen :heart_full: :heart-empty: :heart-empty: :heart-empty: :heart-empty:
    :media-player: Musik machen :heart_full: :heart-half: :heart-empty: :heart-empty: :heart-empty:
    :cup: Scripts machen :heart_full: :heart_full: :heart_full: :heart_full: :heart-break:
    :paper: Story ausdenken :heart_full: :heart_full: :heart_full: :heart-empty: :heart-empty:
    :cut: Pixeln und so :heart-empty: :heart-empty: :heart-empty: :heart-empty: :heart-empty:
    :game: Events proggen :heart_full: :heart_full: :heart_full: :heart_full: :heart_full:
    (Dieser Tab ist rein satirisch.)
  • :folder-open: mes projets

    • :addressbook: Silentium
      :book: Name: Silentium
      :rmxp: Maker: Eigenbau (C++, x86-SSE/AVX-Assembly, Ruby/Lua)

      :paper: Story
      :game: NPCs
      :cup: Scripts
      :drill: Ressis
      :ruler-triangle: Maps
      :compile: Gesamt
      (3+4)% 42 69% 0815 -17.438 103.38% ± 6.3mm²

      (Die Tabelle erfüllt lediglich satirische Zwecke.)
    • :compile: Onyx
      Eine in C++ implementierte, modulare, plattformunabhängige, virtuelle Maschine. Die Test-Version ist bereits halb fertig. Ab dann gibt es vielleicht mehr Infos. Sie soll die auf dem ersten Blick LISP-artige und eigens dafür konstruierte Sprache Obsidian ausführen können. Experimentell wird auch ein Lua-Compiler für Onyx gebaut. Ziel ist eine leistungsfähige, virtuelle Maschine für beliebige Scriptsprachen. Theoretisch gesehen müsste man bloß noch einen kompatiblen Compiler schreiben, der Quellcode jener Sprache in Onyx-Assembly, oder direkt in Onyx-Bytecode übersetzt. Ob die jemand nutzen wird, ist eine andere Frage und nur ein sekundäres... nein, eher tertiäres Ziel dieser VM. Primär dient es mir lediglich dazu, mein Verständnis von Hardware, ISA, und Assembly zu vertiefen, sowie eigene Grenzen auszutesten.

      :exclamation: Warnung!
      Das Entwickeln einer virtuellen Maschine oder Programmiersprache (im wahnsinnigsten Fall beides) ist eine höchst komplizierte Tätigkeit, aus der viel Frust und Hirnmatsche hervor gehen. Sollte sich dennoch ein ähnlich wahnsinniger finden, der sowas zusammen schustern will, so lege ich ihm/ihr die folgenden Bücher ans Herz:
      • Compiler - Das Drachenbuch [978-3-8273-7097-6]
        Dieses Buch schlachtet ausführlich und leicht verständlich die Grundlagen bis hoch zu den Experten-Techniken des Compilerbaus aus. Es fängt mit der Automaten-Theorie und formalen Sprachen an, arbeitet sich durch Analysetechniken vor, und landet schließlich bei Techniken wie Optimierung und Register-Zuweisung. Das Buch wiegt 3Kg oder 4Kg. Hab's mal gewogen. Ist also nicht gerade die Lektüre für unterwegs.

      • Computerarchitektur [3-8273-7016-7]
        Hier werden leicht verständlich die wichtigsten Entwicklungen der Rechnerarchitekturen erklärt (Gut, das Buch ist in die Jahre gekommen, aber der Weg zu heute ist ein winziger Schritt, den man sich nach diesem Buch selbst erdenken kann). Hauptbestandteil des Buchs ist eine relativ umfassende Betrachtung der Funktionsweise dreier gänzlich unterschiedlicher, aber dominierender Prozessor-Typen am Beispiel des Pentium II, UltraSPARC II, sowie picoJava. Die meisten Elemente dieses Buchs sind zwar für die Konstruktion einer virtuellen Maschine irrelevant, oder aufgrund der Tatsache, dass die VM Software ist und z.B. Byte-Grenzen hat, sogar zu Leistungseinbußen führen kann, doch ist ein hinreichendes Verständnis dieser Maschinen, mit denen wir arbeiten, äußerst hilfreich für die Überlegungen, wie die VM arbeiten soll.

      Es kann sehr hilfreich und inspirierend sein, den Code quelloffener, virtueller Maschinen anderer Sprachen zu überfliegen. Meine Lieblings-Quelle war und ist stets die VM von Lua. Sie ist schlank, verständlich, in C implementiert, und basiert im Gegensatz zu vielen anderen Scriptsprachen-VMs auf einer Register-Maschine statt einer Stapelmaschine. Es wäre natürlich vorteilhaft, die entsprechende Sprache zu verstehen, in der man auch die eigene VM implementieren will. Weiterhin ist es äußerst vorteilhaft, eine leistungsstarke und bequeme Sprache wie C++ zu beherrschen, um die VM zu implementieren. Und bevor irgendwer auf die Idee kommt: Assembly ist NICHT als dominierende Sprache für den Bau einer VM geeignet. Wer die Frage des "Warum?" nicht beantworten kann, sollte zunächst die gewählte Sprache und Assembly hinreichend verstehen lernen, und es dann erneut mit der Frage versuchen. Es lohnt sich dennoch, Assembly zu lernen. Allein schon, um erneut das Verständnis zu vertiefen, zumal ihr mehr oder weniger gezwungen seid, auch für eure VM eine Assembler-Sprache zu entwickeln (Außer natürlich ihr schreibt eure Test-Programme Bit für Bit ;3).
  • :locale: enfin

    Je ne peux pas parler français.
    C'est tout ce que Goodle et les restes de cours de français.
Signaturstand: 24.07.2013

Irrlicht

Leuchtendes Irgendwas

Motto: Keep shining!

  • Nachricht senden

7

Samstag, 20. Juli 2013, 20:06

Mmmm, noch zwei Kleinigkeiten:

Du könntest evtl. den Rückgabewert deiner Script... - Methoden durch <true> ersetzen, sonst werden sich viele Scriptbefehle die <nil> zurückgeben in einer Endlosschleife ausgeführt.
Der Runtime-Error wird übrigens auch ausgelöst wenn man z.B. durch den o.g. Workaround versucht auf eine Instanzvariable zuzugreifen.

Ansonsten könnte man die Scriptkommandos z.B. auch in Methoden übersetzen, dann gäbe es die Probleme mit den Interpretermethoden/-instanzvariablen nicht:
(vereinfacht)
Spoiler

Ruby Quellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
module InterpreterScriptCalls
 
  @@scriptcommands = Hash.new { |h, k| prepare_script(k) }
 
  def self.prepare_script(code)
    sym = :"$#{@@scriptcommands.length + 1}th_script"
    eval("define_method(:'#{sym}') { #{code} \n }")
    return (@@scriptcommands[code] = sym)
  end
 
  def eval(code, *args)
    return args.empty? ? send(@@scriptcommands[code]) : super(code, *args)
  end
end
 
class Interpreter
  include InterpreterScriptCalls
end
zum Lesen den Text mit der Maus markieren

Der Imperator

Oberschurke im Ruhestand

Motto: "Satzzeichen sind keine Rudeltiere." - Chesra

  • Nachricht senden

8

Samstag, 20. Juli 2013, 20:33

Zitat

Du könntest evtl. den Rückgabewert deiner Script... - Methoden durch <true> ersetzen [...]
Nein. RGSS wird sich schon was dabei gedacht haben. Und der Gedanke ist, dass bei nil/false irgendwas schief lief, so dass das Script einen erneuten Versuch bräuchte.

Zitat

Der Runtime-Error wird übrigens auch ausgelöst wenn man z.B. durch den o.g. Workaround versucht auf eine Instanzvariable zuzugreifen.
Err... ja? Das ist beabsichtigt, falls du sowas wie @wait_count = 42 meinst. Falls du this_interpreter.instance_variable_get(:@wait_count) o.Ä. meinst... nein? Kein bei mir reproduzierbarer RuntimeError? Beispielcode? String-Vergleiche sind im Übrigen nicht sonderlich flott.
  • :medal: Werbung

    Bild

    Cpp Quellcode

    1
    
    #define TRUE FALSE //Happy debugging suckers
    (Einfach nur wundervoll.)
  • :palette: 1plus3 :cake:

    Bild
  • :fires: Nuuuhminaaah

    Bild
  • :medal: compétences

    mes compétences
    :heart_full: :heart_full: :heart_full: :heart_full: :heart_full: max.
    :ruler-triangle: Maps machen :heart_full: :heart-empty: :heart-empty: :heart-empty: :heart-empty:
    :media-player: Musik machen :heart_full: :heart-half: :heart-empty: :heart-empty: :heart-empty:
    :cup: Scripts machen :heart_full: :heart_full: :heart_full: :heart_full: :heart-break:
    :paper: Story ausdenken :heart_full: :heart_full: :heart_full: :heart-empty: :heart-empty:
    :cut: Pixeln und so :heart-empty: :heart-empty: :heart-empty: :heart-empty: :heart-empty:
    :game: Events proggen :heart_full: :heart_full: :heart_full: :heart_full: :heart_full:
    (Dieser Tab ist rein satirisch.)
  • :folder-open: mes projets

    • :addressbook: Silentium
      :book: Name: Silentium
      :rmxp: Maker: Eigenbau (C++, x86-SSE/AVX-Assembly, Ruby/Lua)

      :paper: Story
      :game: NPCs
      :cup: Scripts
      :drill: Ressis
      :ruler-triangle: Maps
      :compile: Gesamt
      (3+4)% 42 69% 0815 -17.438 103.38% ± 6.3mm²

      (Die Tabelle erfüllt lediglich satirische Zwecke.)
    • :compile: Onyx
      Eine in C++ implementierte, modulare, plattformunabhängige, virtuelle Maschine. Die Test-Version ist bereits halb fertig. Ab dann gibt es vielleicht mehr Infos. Sie soll die auf dem ersten Blick LISP-artige und eigens dafür konstruierte Sprache Obsidian ausführen können. Experimentell wird auch ein Lua-Compiler für Onyx gebaut. Ziel ist eine leistungsfähige, virtuelle Maschine für beliebige Scriptsprachen. Theoretisch gesehen müsste man bloß noch einen kompatiblen Compiler schreiben, der Quellcode jener Sprache in Onyx-Assembly, oder direkt in Onyx-Bytecode übersetzt. Ob die jemand nutzen wird, ist eine andere Frage und nur ein sekundäres... nein, eher tertiäres Ziel dieser VM. Primär dient es mir lediglich dazu, mein Verständnis von Hardware, ISA, und Assembly zu vertiefen, sowie eigene Grenzen auszutesten.

      :exclamation: Warnung!
      Das Entwickeln einer virtuellen Maschine oder Programmiersprache (im wahnsinnigsten Fall beides) ist eine höchst komplizierte Tätigkeit, aus der viel Frust und Hirnmatsche hervor gehen. Sollte sich dennoch ein ähnlich wahnsinniger finden, der sowas zusammen schustern will, so lege ich ihm/ihr die folgenden Bücher ans Herz:
      • Compiler - Das Drachenbuch [978-3-8273-7097-6]
        Dieses Buch schlachtet ausführlich und leicht verständlich die Grundlagen bis hoch zu den Experten-Techniken des Compilerbaus aus. Es fängt mit der Automaten-Theorie und formalen Sprachen an, arbeitet sich durch Analysetechniken vor, und landet schließlich bei Techniken wie Optimierung und Register-Zuweisung. Das Buch wiegt 3Kg oder 4Kg. Hab's mal gewogen. Ist also nicht gerade die Lektüre für unterwegs.

      • Computerarchitektur [3-8273-7016-7]
        Hier werden leicht verständlich die wichtigsten Entwicklungen der Rechnerarchitekturen erklärt (Gut, das Buch ist in die Jahre gekommen, aber der Weg zu heute ist ein winziger Schritt, den man sich nach diesem Buch selbst erdenken kann). Hauptbestandteil des Buchs ist eine relativ umfassende Betrachtung der Funktionsweise dreier gänzlich unterschiedlicher, aber dominierender Prozessor-Typen am Beispiel des Pentium II, UltraSPARC II, sowie picoJava. Die meisten Elemente dieses Buchs sind zwar für die Konstruktion einer virtuellen Maschine irrelevant, oder aufgrund der Tatsache, dass die VM Software ist und z.B. Byte-Grenzen hat, sogar zu Leistungseinbußen führen kann, doch ist ein hinreichendes Verständnis dieser Maschinen, mit denen wir arbeiten, äußerst hilfreich für die Überlegungen, wie die VM arbeiten soll.

      Es kann sehr hilfreich und inspirierend sein, den Code quelloffener, virtueller Maschinen anderer Sprachen zu überfliegen. Meine Lieblings-Quelle war und ist stets die VM von Lua. Sie ist schlank, verständlich, in C implementiert, und basiert im Gegensatz zu vielen anderen Scriptsprachen-VMs auf einer Register-Maschine statt einer Stapelmaschine. Es wäre natürlich vorteilhaft, die entsprechende Sprache zu verstehen, in der man auch die eigene VM implementieren will. Weiterhin ist es äußerst vorteilhaft, eine leistungsstarke und bequeme Sprache wie C++ zu beherrschen, um die VM zu implementieren. Und bevor irgendwer auf die Idee kommt: Assembly ist NICHT als dominierende Sprache für den Bau einer VM geeignet. Wer die Frage des "Warum?" nicht beantworten kann, sollte zunächst die gewählte Sprache und Assembly hinreichend verstehen lernen, und es dann erneut mit der Frage versuchen. Es lohnt sich dennoch, Assembly zu lernen. Allein schon, um erneut das Verständnis zu vertiefen, zumal ihr mehr oder weniger gezwungen seid, auch für eure VM eine Assembler-Sprache zu entwickeln (Außer natürlich ihr schreibt eure Test-Programme Bit für Bit ;3).
  • :locale: enfin

    Je ne peux pas parler français.
    C'est tout ce que Goodle et les restes de cours de français.
Signaturstand: 24.07.2013

Irrlicht

Leuchtendes Irgendwas

Motto: Keep shining!

  • Nachricht senden

9

Samstag, 20. Juli 2013, 21:03

Irgendwas wird sich RGSS schon dabei gedacht haben, allerdings ist es fraglich ob es beabsichtigt war dass einzeilige Scriptbefehle wiederholt werden wenn sie <false> ergeben, mehrzeilige aber nur eine kurze Pause verursachen (das gilt auch nur für Befehle die explizit <false> ergeben, <nil> und alle anderen Rückgabewerte erzeugen keine Wartezeit).

Zum RuntimeError:
Doch genau sowas (this_interpreter.instance_variable_get(:@wait_count)) meine ich:
Project65.exe
Aber was sollte if script['@'] auch sonst bewirken?

Zum Thema String-Vergleiche:
Du hast natürlich recht, aber bei ~400 Zeichen die theoretisch in eine Scriptbox passen sollte der Mehraufwand eig. überschaubar bleiben. Das Ganze ist wie angemerkt etwas vereinfacht.

Dieser Beitrag wurde bereits 8 mal editiert, zuletzt von »Irrlicht« (20. Juli 2013, 22:23)


Der Imperator

Oberschurke im Ruhestand

Motto: "Satzzeichen sind keine Rudeltiere." - Chesra

  • Nachricht senden

10

Samstag, 20. Juli 2013, 23:06

Ach, ich bin doch doof. Habe das Stückchen mal entfernt. Ich vergaß, dass Symbole zu Instanz-Variablen auch das @ brauchen, als ich den Code bastelte. Muss man halt aufpassen, da Ruby seltsam genug ist, um Instanzvariablen aus dem Nichts zu ziehen. Aber gut, die Einzeiler sind wahrlich seltsam. Man könnte natürlich das &&true||false durch ein einsames true in der letzten Zeile ersetzen.
  • :medal: Werbung

    Bild

    Cpp Quellcode

    1
    
    #define TRUE FALSE //Happy debugging suckers
    (Einfach nur wundervoll.)
  • :palette: 1plus3 :cake:

    Bild
  • :fires: Nuuuhminaaah

    Bild
  • :medal: compétences

    mes compétences
    :heart_full: :heart_full: :heart_full: :heart_full: :heart_full: max.
    :ruler-triangle: Maps machen :heart_full: :heart-empty: :heart-empty: :heart-empty: :heart-empty:
    :media-player: Musik machen :heart_full: :heart-half: :heart-empty: :heart-empty: :heart-empty:
    :cup: Scripts machen :heart_full: :heart_full: :heart_full: :heart_full: :heart-break:
    :paper: Story ausdenken :heart_full: :heart_full: :heart_full: :heart-empty: :heart-empty:
    :cut: Pixeln und so :heart-empty: :heart-empty: :heart-empty: :heart-empty: :heart-empty:
    :game: Events proggen :heart_full: :heart_full: :heart_full: :heart_full: :heart_full:
    (Dieser Tab ist rein satirisch.)
  • :folder-open: mes projets

    • :addressbook: Silentium
      :book: Name: Silentium
      :rmxp: Maker: Eigenbau (C++, x86-SSE/AVX-Assembly, Ruby/Lua)

      :paper: Story
      :game: NPCs
      :cup: Scripts
      :drill: Ressis
      :ruler-triangle: Maps
      :compile: Gesamt
      (3+4)% 42 69% 0815 -17.438 103.38% ± 6.3mm²

      (Die Tabelle erfüllt lediglich satirische Zwecke.)
    • :compile: Onyx
      Eine in C++ implementierte, modulare, plattformunabhängige, virtuelle Maschine. Die Test-Version ist bereits halb fertig. Ab dann gibt es vielleicht mehr Infos. Sie soll die auf dem ersten Blick LISP-artige und eigens dafür konstruierte Sprache Obsidian ausführen können. Experimentell wird auch ein Lua-Compiler für Onyx gebaut. Ziel ist eine leistungsfähige, virtuelle Maschine für beliebige Scriptsprachen. Theoretisch gesehen müsste man bloß noch einen kompatiblen Compiler schreiben, der Quellcode jener Sprache in Onyx-Assembly, oder direkt in Onyx-Bytecode übersetzt. Ob die jemand nutzen wird, ist eine andere Frage und nur ein sekundäres... nein, eher tertiäres Ziel dieser VM. Primär dient es mir lediglich dazu, mein Verständnis von Hardware, ISA, und Assembly zu vertiefen, sowie eigene Grenzen auszutesten.

      :exclamation: Warnung!
      Das Entwickeln einer virtuellen Maschine oder Programmiersprache (im wahnsinnigsten Fall beides) ist eine höchst komplizierte Tätigkeit, aus der viel Frust und Hirnmatsche hervor gehen. Sollte sich dennoch ein ähnlich wahnsinniger finden, der sowas zusammen schustern will, so lege ich ihm/ihr die folgenden Bücher ans Herz:
      • Compiler - Das Drachenbuch [978-3-8273-7097-6]
        Dieses Buch schlachtet ausführlich und leicht verständlich die Grundlagen bis hoch zu den Experten-Techniken des Compilerbaus aus. Es fängt mit der Automaten-Theorie und formalen Sprachen an, arbeitet sich durch Analysetechniken vor, und landet schließlich bei Techniken wie Optimierung und Register-Zuweisung. Das Buch wiegt 3Kg oder 4Kg. Hab's mal gewogen. Ist also nicht gerade die Lektüre für unterwegs.

      • Computerarchitektur [3-8273-7016-7]
        Hier werden leicht verständlich die wichtigsten Entwicklungen der Rechnerarchitekturen erklärt (Gut, das Buch ist in die Jahre gekommen, aber der Weg zu heute ist ein winziger Schritt, den man sich nach diesem Buch selbst erdenken kann). Hauptbestandteil des Buchs ist eine relativ umfassende Betrachtung der Funktionsweise dreier gänzlich unterschiedlicher, aber dominierender Prozessor-Typen am Beispiel des Pentium II, UltraSPARC II, sowie picoJava. Die meisten Elemente dieses Buchs sind zwar für die Konstruktion einer virtuellen Maschine irrelevant, oder aufgrund der Tatsache, dass die VM Software ist und z.B. Byte-Grenzen hat, sogar zu Leistungseinbußen führen kann, doch ist ein hinreichendes Verständnis dieser Maschinen, mit denen wir arbeiten, äußerst hilfreich für die Überlegungen, wie die VM arbeiten soll.

      Es kann sehr hilfreich und inspirierend sein, den Code quelloffener, virtueller Maschinen anderer Sprachen zu überfliegen. Meine Lieblings-Quelle war und ist stets die VM von Lua. Sie ist schlank, verständlich, in C implementiert, und basiert im Gegensatz zu vielen anderen Scriptsprachen-VMs auf einer Register-Maschine statt einer Stapelmaschine. Es wäre natürlich vorteilhaft, die entsprechende Sprache zu verstehen, in der man auch die eigene VM implementieren will. Weiterhin ist es äußerst vorteilhaft, eine leistungsstarke und bequeme Sprache wie C++ zu beherrschen, um die VM zu implementieren. Und bevor irgendwer auf die Idee kommt: Assembly ist NICHT als dominierende Sprache für den Bau einer VM geeignet. Wer die Frage des "Warum?" nicht beantworten kann, sollte zunächst die gewählte Sprache und Assembly hinreichend verstehen lernen, und es dann erneut mit der Frage versuchen. Es lohnt sich dennoch, Assembly zu lernen. Allein schon, um erneut das Verständnis zu vertiefen, zumal ihr mehr oder weniger gezwungen seid, auch für eure VM eine Assembler-Sprache zu entwickeln (Außer natürlich ihr schreibt eure Test-Programme Bit für Bit ;3).
  • :locale: enfin

    Je ne peux pas parler français.
    C'est tout ce que Goodle et les restes de cours de français.
Signaturstand: 24.07.2013

Social Bookmarks