Skip to content

Commit d26d4e6

Browse files
committed
feat: add multi-file JES loading support with comma/semicolon-separated paths and extract loadJes method
1 parent 3f195bf commit d26d4e6

File tree

12 files changed

+344
-486
lines changed

12 files changed

+344
-486
lines changed

runtime/src/main/java/com/jvn/runtime/JvnApp.java

Lines changed: 41 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import com.jvn.fx.audio.FxAudioService;
1818
import com.jvn.core.audio.AudioFacade;
1919
import com.jvn.scripting.jes.JesLoader;
20+
import com.jvn.scripting.jes.runtime.JesScene2D;
2021
import org.slf4j.Logger;
2122
import org.slf4j.LoggerFactory;
2223
import java.io.ByteArrayInputStream;
@@ -102,28 +103,7 @@ public static void main(String[] args) {
102103
engine.start();
103104

104105
if (jesScript != null) {
105-
try {
106-
AssetCatalog cat = new AssetCatalog();
107-
InputStream in = cat.open(com.jvn.core.assets.AssetType.SCRIPT, jesScript);
108-
var scene = JesLoader.load(in);
109-
new JesVnBridge(engine).attach(scene);
110-
engine.scenes().push(scene);
111-
} catch (Exception e) {
112-
log.warn("Failed to load JES script '{}': {}. Loading inline sample.", jesScript, e.toString());
113-
try {
114-
String sample = "scene \"Sample\" {\n" +
115-
" entity \"panel\" {\n" +
116-
" component Panel2D { x: 0.2 y: 0.2 w: 1.0 h: 0.6 fill: rgb(0.1,0.6,0.2,0.8) }\n" +
117-
" }\n" +
118-
"}\n";
119-
var in2 = new ByteArrayInputStream(sample.getBytes());
120-
var scene = JesLoader.load(in2);
121-
new JesVnBridge(engine).attach(scene);
122-
engine.scenes().push(scene);
123-
} catch (Exception ex) {
124-
log.warn("Inline JES sample failed: {}", ex.toString());
125-
}
126-
}
106+
loadJes(engine, jesScript);
127107
} else if (launchBilliards) {
128108
log.warn("Billiards module is not available; ignoring --billiards flag.");
129109
} else {
@@ -163,4 +143,43 @@ public static void main(String[] args) {
163143
FxLauncher.launch(engine);
164144
}
165145
}
146+
147+
private static void loadJes(Engine engine, String jesScript) {
148+
try {
149+
AssetCatalog cat = new AssetCatalog();
150+
String[] parts = jesScript.split("[,;]");
151+
if (parts.length == 1) {
152+
try (InputStream in = cat.open(AssetType.SCRIPT, jesScript)) {
153+
JesScene2D scene = JesLoader.load(in);
154+
new JesVnBridge(engine).attach(scene);
155+
engine.scenes().push(scene);
156+
}
157+
} else {
158+
java.util.List<InputStream> ins = new java.util.ArrayList<>();
159+
for (String p : parts) {
160+
String path = p.trim();
161+
if (path.isEmpty()) continue;
162+
ins.add(cat.open(AssetType.SCRIPT, path));
163+
}
164+
JesScene2D scene = JesLoader.loadMerged(ins);
165+
new JesVnBridge(engine).attach(scene);
166+
engine.scenes().push(scene);
167+
}
168+
} catch (Exception e) {
169+
log.warn("Failed to load JES script '{}': {}. Loading inline sample.", jesScript, e.toString());
170+
try {
171+
String sample = "scene \"Sample\" {\n" +
172+
" entity \"panel\" {\n" +
173+
" component Panel2D { x: 0.2 y: 0.2 w: 1.0 h: 0.6 fill: rgb(0.1,0.6,0.2,0.8) }\n" +
174+
" }\n" +
175+
"}\n";
176+
var in2 = new ByteArrayInputStream(sample.getBytes());
177+
var scene = JesLoader.load(in2);
178+
new JesVnBridge(engine).attach(scene);
179+
engine.scenes().push(scene);
180+
} catch (Exception ex) {
181+
log.warn("Inline JES sample failed: {}", ex.toString());
182+
}
183+
}
184+
}
166185
}

samples/billiards.jes

Lines changed: 0 additions & 231 deletions
This file was deleted.

samples/billiards/base.jes

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
scene "billiards_base" {
2+
# Base table geometry and balls (normalized coordinates 0..1)
3+
entity "table" { component Panel2D { x: 0 y: 0 w: 1 h: 1 fill: rgb(0.05,0.25,0.08,1) } }
4+
5+
# Rails
6+
entity "rail_top" { component PhysicsBody2D { shape: box x: 0.05 y: 0.05 w: 0.9 h: 0.02 static: true restitution: 1.0 } }
7+
entity "rail_bottom" { component PhysicsBody2D { shape: box x: 0.05 y: 0.93 w: 0.9 h: 0.02 static: true restitution: 1.0 } }
8+
entity "rail_left" { component PhysicsBody2D { shape: box x: 0.05 y: 0.07 w: 0.02 h: 0.86 static: true restitution: 1.0 } }
9+
entity "rail_right" { component PhysicsBody2D { shape: box x: 0.93 y: 0.07 w: 0.02 h: 0.86 static: true restitution: 1.0 } }
10+
11+
# Pockets as sensors with a call hook to pocketBall
12+
entity "pocket_tl" { component PhysicsBody2D { shape: circle x: 0.05 y: 0.05 r: 0.03 static: true sensor: true onTrigger: "pocketBall" } }
13+
entity "pocket_tr" { component PhysicsBody2D { shape: circle x: 0.95 y: 0.05 r: 0.03 static: true sensor: true onTrigger: "pocketBall" } }
14+
entity "pocket_bl" { component PhysicsBody2D { shape: circle x: 0.05 y: 0.95 r: 0.03 static: true sensor: true onTrigger: "pocketBall" } }
15+
entity "pocket_br" { component PhysicsBody2D { shape: circle x: 0.95 y: 0.95 r: 0.03 static: true sensor: true onTrigger: "pocketBall" } }
16+
entity "pocket_cl" { component PhysicsBody2D { shape: circle x: 0.05 y: 0.5 r: 0.03 static: true sensor: true onTrigger: "pocketBall" } }
17+
entity "pocket_cr" { component PhysicsBody2D { shape: circle x: 0.95 y: 0.5 r: 0.03 static: true sensor: true onTrigger: "pocketBall" } }
18+
19+
# Cue ball
20+
entity "cue_ball" {
21+
component PhysicsBody2D {
22+
shape: circle
23+
x: 0.25 y: 0.5 r: 0.015
24+
restitution: 0.98
25+
vx: 0.9 vy: 0.0
26+
color: rgb(1,1,1,1)
27+
}
28+
}
29+
30+
# Rack
31+
entity "b1" { component PhysicsBody2D { shape: circle x: 0.65 y: 0.50 r: 0.015 restitution: 0.98 color: rgb(1,0.8,0.2,1) } }
32+
entity "b2" { component PhysicsBody2D { shape: circle x: 0.68 y: 0.515 r: 0.015 restitution: 0.98 color: rgb(0.2,0.8,1,1) } }
33+
entity "b3" { component PhysicsBody2D { shape: circle x: 0.68 y: 0.485 r: 0.015 restitution: 0.98 color: rgb(0.9,0.2,0.2,1) } }
34+
entity "b4" { component PhysicsBody2D { shape: circle x: 0.71 y: 0.53 r: 0.015 restitution: 0.98 color: rgb(0.2,0.9,0.5,1) } }
35+
entity "b5" { component PhysicsBody2D { shape: circle x: 0.71 y: 0.50 r: 0.015 restitution: 0.98 color: rgb(0.9,0.6,0.2,1) } }
36+
entity "b6" { component PhysicsBody2D { shape: circle x: 0.71 y: 0.47 r: 0.015 restitution: 0.98 color: rgb(0.5,0.4,0.9,1) } }
37+
entity "b7" { component PhysicsBody2D { shape: circle x: 0.74 y: 0.545 r: 0.015 restitution: 0.98 color: rgb(0.3,0.7,0.9,1) } }
38+
entity "b8" { component PhysicsBody2D { shape: circle x: 0.74 y: 0.515 r: 0.015 restitution: 0.98 color: rgb(0.9,0.3,0.6,1) } }
39+
entity "b9" { component PhysicsBody2D { shape: circle x: 0.74 y: 0.485 r: 0.015 restitution: 0.98 color: rgb(0.7,0.7,0.2,1) } }
40+
entity "b10" { component PhysicsBody2D { shape: circle x: 0.74 y: 0.455 r: 0.015 restitution: 0.98 color: rgb(0.2,0.9,0.2,1) } }
41+
}

samples/billiards/game.jes

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
scene "billiards_game" {
2+
# Engineered single-file billiards scene (no merging required)
3+
4+
# Background / rails / pockets
5+
entity "table" { component Panel2D { x: 0 y: 0 w: 1 h: 1 fill: rgb(0.05,0.25,0.08,1) } }
6+
entity "rail_top" { component PhysicsBody2D { shape: box x: 0.05 y: 0.05 w: 0.9 h: 0.02 static: true restitution: 1.0 } }
7+
entity "rail_bottom" { component PhysicsBody2D { shape: box x: 0.05 y: 0.93 w: 0.9 h: 0.02 static: true restitution: 1.0 } }
8+
entity "rail_left" { component PhysicsBody2D { shape: box x: 0.05 y: 0.07 w: 0.02 h: 0.86 static: true restitution: 1.0 } }
9+
entity "rail_right" { component PhysicsBody2D { shape: box x: 0.93 y: 0.07 w: 0.02 h: 0.86 static: true restitution: 1.0 } }
10+
11+
# Pockets with onTrigger -> pocketBall (built-in)
12+
entity "pocket_tl" { component PhysicsBody2D { shape: circle x: 0.05 y: 0.05 r: 0.03 static: true sensor: true onTrigger: "pocketBall" } }
13+
entity "pocket_tr" { component PhysicsBody2D { shape: circle x: 0.95 y: 0.05 r: 0.03 static: true sensor: true onTrigger: "pocketBall" } }
14+
entity "pocket_bl" { component PhysicsBody2D { shape: circle x: 0.05 y: 0.95 r: 0.03 static: true sensor: true onTrigger: "pocketBall" } }
15+
entity "pocket_br" { component PhysicsBody2D { shape: circle x: 0.95 y: 0.95 r: 0.03 static: true sensor: true onTrigger: "pocketBall" } }
16+
entity "pocket_cl" { component PhysicsBody2D { shape: circle x: 0.05 y: 0.5 r: 0.03 static: true sensor: true onTrigger: "pocketBall" } }
17+
entity "pocket_cr" { component PhysicsBody2D { shape: circle x: 0.95 y: 0.5 r: 0.03 static: true sensor: true onTrigger: "pocketBall" } }
18+
19+
# HUD
20+
entity "title" { component Label2D { text: "BILLIARDS" x: 0.5 y: 0.08 size: 22 bold: true align: center color: rgb(1,1,1,1) } }
21+
entity "hint" { component Label2D { text: "Sensors pocket balls • R to reset rack" x: 0.5 y: 0.12 size: 14 bold: false align: center color: rgb(0.8,0.9,1,0.9) } }
22+
entity "score_label" { component Label2D { text: "Score: 0" x: 0.5 y: 0.16 size: 12 bold: true align: center color: rgb(1,1,1,0.9) } }
23+
entity "panel_left" { component Panel2D { x: 0.02 y: 0.02 w: 0.12 h: 0.12 fill: rgb(0,0,0,0.35) } }
24+
entity "panel_right" { component Panel2D { x: 0.86 y: 0.02 w: 0.12 h: 0.12 fill: rgb(0,0,0,0.35) } }
25+
26+
# Balls
27+
entity "cue_ball" { component PhysicsBody2D { shape: circle x: 0.25 y: 0.5 r: 0.015 restitution: 0.98 vx: 0.9 vy: 0.0 color: rgb(1,1,1,1) } }
28+
entity "b1" { component PhysicsBody2D { shape: circle x: 0.65 y: 0.50 r: 0.015 restitution: 0.98 color: rgb(1,0.8,0.2,1) } }
29+
entity "b2" { component PhysicsBody2D { shape: circle x: 0.68 y: 0.515 r: 0.015 restitution: 0.98 color: rgb(0.2,0.8,1,1) } }
30+
entity "b3" { component PhysicsBody2D { shape: circle x: 0.68 y: 0.485 r: 0.015 restitution: 0.98 color: rgb(0.9,0.2,0.2,1) } }
31+
entity "b4" { component PhysicsBody2D { shape: circle x: 0.71 y: 0.53 r: 0.015 restitution: 0.98 color: rgb(0.2,0.9,0.5,1) } }
32+
entity "b5" { component PhysicsBody2D { shape: circle x: 0.71 y: 0.50 r: 0.015 restitution: 0.98 color: rgb(0.9,0.6,0.2,1) } }
33+
entity "b6" { component PhysicsBody2D { shape: circle x: 0.71 y: 0.47 r: 0.015 restitution: 0.98 color: rgb(0.5,0.4,0.9,1) } }
34+
entity "b7" { component PhysicsBody2D { shape: circle x: 0.74 y: 0.545 r: 0.015 restitution: 0.98 color: rgb(0.3,0.7,0.9,1) } }
35+
entity "b8" { component PhysicsBody2D { shape: circle x: 0.74 y: 0.515 r: 0.015 restitution: 0.98 color: rgb(0.9,0.3,0.6,1) } }
36+
entity "b9" { component PhysicsBody2D { shape: circle x: 0.74 y: 0.485 r: 0.015 restitution: 0.98 color: rgb(0.7,0.7,0.2,1) } }
37+
entity "b10" { component PhysicsBody2D { shape: circle x: 0.74 y: 0.455 r: 0.015 restitution: 0.98 color: rgb(0.2,0.9,0.2,1) } }
38+
39+
# Reset binding to restore rack/score
40+
on key "R" do resetBalls
41+
}

0 commit comments

Comments
 (0)