summaryrefslogtreecommitdiffhomepage
path: root/mrbgems/flecs/mrblib/flecs.rb
blob: bc7762ababdae9e3d602b22ad2b8e8eb3daf9cd7 (plain)
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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# Idiomatic Ruby surface for flecs, layered on the low-level Flecs::World#_*
# primitives (see src/flecs_bindings.c). Components are declared at runtime via
# the meta addon and (de)serialized to/from Ruby Hashes.
#
#   world = Flecs::World.new
#   pos   = world.struct("Position", "{float x; float y;}")
#   vel   = world.struct("Velocity", "{float x; float y;}")
#   e = world.entity("player").set(pos, x: 0, y: 0).set(vel, x: 1, y: 2)
#
#   world.system("Move", with: [pos, vel]) do |id, p, v|
#     p[:x] += v[:x]; p[:y] += v[:y]      # mutations to p are written back
#   end
#   world.progress(1.0/60)

module Flecs
  class World
    # Create an entity (optionally named). Returns a Flecs::Entity.
    def entity(name = nil)
      Entity.new(self, _entity(name && name.to_s))
    end

    # Declare a component as a C struct from a meta descriptor string, e.g.
    #   world.struct("Position", "{float x; float y;}")
    # Returns a Flecs::Component (usable wherever an id is expected).
    def struct(name, descriptor)
      Component.new(self, _struct(name.to_s, descriptor))
    end
    alias component struct

    # A tag is a dataless entity used as an id (add/remove/has).
    def tag(name)
      Component.new(self, _entity(name.to_s))
    end

    # Look up an entity/component by name -> Flecs::Entity | nil.
    def lookup(name)
      id = _lookup(name.to_s)
      id && Entity.new(self, id)
    end

    # Wrap a raw entity id (e.g. one yielded to a system/query block) in a
    # Flecs::Entity so you can call set/get/add/... on it.
    def entity_for(id)
      Entity.new(self, id.to_i)
    end

    # Cached query over the given component/tag ids -> Flecs::Query.
    def query(*components)
      _query(components.flatten.map(&:to_i))
    end

    # Register a system that runs each progress() during `phase`.
    # Block receives |entity_id, *component_hashes| per matched entity;
    # mutations to the component hashes are written back.
    def system(name, with:, phase: Flecs::ON_UPDATE, &block)
      _system(name.to_s, phase.to_i, Array(with).map(&:to_i), &block)
    end

    # Advance the world by dt seconds, running all systems. Returns false when
    # the world wants to quit.
    def progress(dt = 0.0)
      _progress(dt)
    end

    # Observability (R5): start the flecs REST API so the hosted Flecs Explorer
    # (flecs.dev/explorer?host=localhost:<port>) can inspect the live world.
    # Served during progress. Dev-only (opens a local port).
    def enable_rest(port = 27750)
      _enable_rest(port.to_i); self
    end

    # Per-system timing + world monitor stats (shown in the Explorer).
    def enable_stats
      _enable_stats; self
    end

    # Query the flecs REST API in-process (no socket — works on desktop AND web).
    # Requires enable_rest first. Returns raw JSON string.
    #   world.rest_request("GET", "/world")
    #   world.rest_request("GET", "/query?expr=Position&values=true")
    def rest_request(method, path, body = "")
      _rest_request(method.to_s, path.to_s, body.to_s)
    end
  end

  # Lightweight wrapper around an entity id bound to its world.
  class Entity
    attr_reader :id, :world
    def initialize(world, id); @world = world; @id = id; end
    def to_i; @id; end
    def to_int; @id; end

    def name;      @world._name(@id); end
    def name=(n);  @world._set_name(@id, n.to_s); end
    def delete;    @world._delete(@id); end
    def alive?;    @world._alive?(@id); end

    # set(comp, x: 1, y: 2) or set(comp, {x: 1, y: 2})
    def set(comp, fields = nil, **kw)
      @world._set(@id, comp.to_i, fields || kw); self
    end
    def get(comp);    @world._get(@id, comp.to_i); end   # -> Hash | nil
    def add(comp);    @world._add(@id, comp.to_i); self; end
    def remove(comp); @world._remove(@id, comp.to_i); self; end
    def has?(comp);   @world._has?(@id, comp.to_i); end

    def ==(other); other.respond_to?(:to_i) && other.to_i == @id; end
    def inspect; "#<Flecs::Entity #{@id}#{name ? " #{name.inspect}" : ''}>"; end
  end

  # Wrapper around a component/tag id (also just an entity under the hood).
  class Component
    attr_reader :id, :world
    def initialize(world, id); @world = world; @id = id; end
    def to_i; @id; end
    def to_int; @id; end
    def name; @world._name(@id); end
    def inspect; "#<Flecs::Component #{@id} #{name.inspect}>"; end
  end

  class Query
    include Enumerable
    # each { |entity_id, *component_hashes| ... } ; mutations written back.
    def each(&block); _each(&block); self; end
  end
end