Skip to content

Commit 0c9343e

Browse files
committed
misc fixes
1 parent 7b10f26 commit 0c9343e

File tree

12 files changed

+250
-288
lines changed

12 files changed

+250
-288
lines changed

build.gradle

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,12 @@ tasks.withType(JavaCompile).configureEach {
4545
options.release = 21
4646
}
4747

48+
tasks.named('processResources') {
49+
filesMatching('plugin.yml') {
50+
expand(version: project.version)
51+
}
52+
}
53+
4854
tasks.named('jar') {
4955
archiveFileName = "${project.name}-${project.version}.jar"
5056
}

changelog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ Feature update
5454
#### Misc
5555

5656
- Added dev versioning for non-release commits
57+
- Added wiki site
5758

5859
## 1D
5960

docs/site/config.html

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<meta name="viewport" content="width=device-width, initial-scale=1.0">
66
<title>Config — PyJavaBridge</title>
77
<meta property="og:title" content="Config — PyJavaBridge">
8-
<meta property="og:description" content="YAML configuration files">
8+
<meta property="og:description" content="Configuration files (TOML, JSON, properties)">
99
<meta property="og:type" content="website">
1010
<meta property="og:site_name" content="PyJavaBridge Docs">
1111
<meta name="theme-color" content="#6366f1">
@@ -153,25 +153,33 @@
153153
<!-- Main -->
154154
<main class="main">
155155
<div class="content">
156-
<p class="subtitle">YAML configuration files</p>
156+
<p class="subtitle">Configuration files (TOML, JSON, properties)</p>
157157
<h1 id="config">Config</h1>
158-
<p><code>Config</code> provides a simple interface for reading and writing YAML configuration files. Config files are stored in the plugin's data folder and persist across server restarts.</p>
158+
<p><code>Config</code> provides a simple interface for reading and writing configuration files. Config files are stored in the plugin's data folder and persist across server restarts.</p>
159+
<p>Supported formats: <strong>TOML</strong> (default), <strong>JSON</strong>, and <strong>properties</strong>.</p>
159160
<hr />
160161
<h2 id="constructor">Constructor</h2>
161-
<pre><code class="language-python">Config(name=<span class="kw">None</span>, defaults=<span class="kw">None</span>)
162+
<pre><code class="language-python">Config(name=<span class="kw">None</span>, defaults=<span class="kw">None</span>, format=<span class="st">&quot;toml&quot;</span>)
162163
</code></pre>
163164
<p>Load or create a configuration file.</p>
164165
<ul>
165166
<li><strong>Parameters:</strong></li>
166-
<li><code>name</code> (<code>str | None</code>) — File name (without <code>.yml</code> extension). If <code>None</code>, uses the default <code>config.yml</code>.</li>
167+
<li><code>name</code> (<code>str | None</code>) — File name (without extension). If <code>None</code>, uses the script name.</li>
167168
<li><code>defaults</code> (<code>dict[str, Any] | None</code>) — Default values to merge into the config if they don't already exist.</li>
169+
<li><code>format</code> (<code>str</code>) — File format: <code>"toml"</code> (default), <code>"json"</code>, or <code>"properties"</code>.</li>
168170
</ul>
169-
<pre><code class="language-python"><span class="cm"># Default config.yml</span>
171+
<pre><code class="language-python"><span class="cm"># Default config.toml</span>
170172
config = Config()
171173

172174
<span class="cm"># Named config</span>
173175
bans = Config(<span class="st">&quot;bans&quot;</span>)
174176

177+
<span class="cm"># JSON format</span>
178+
data = Config(<span class="st">&quot;data&quot;</span>, format=<span class="st">&quot;json&quot;</span>)
179+
180+
<span class="cm"># Properties format</span>
181+
props = Config(<span class="st">&quot;server&quot;</span>, format=<span class="st">&quot;properties&quot;</span>)
182+
175183
<span class="cm"># With defaults</span>
176184
settings = Config(<span class="st">&quot;settings&quot;</span>, defaults={
177185
<span class="st">&quot;spawn_protection_radius&quot;</span>: <span class="nb">16</span>,
@@ -190,7 +198,7 @@ <h3 id="path">path</h3>
190198
<ul>
191199
<li><strong>Type:</strong> <code>str</code></li>
192200
</ul>
193-
<p>Absolute file path to the YAML file.</p>
201+
<p>Absolute file path to the config file.</p>
194202
<hr />
195203
<h2 id="methods">Methods</h2>
196204
<h3 id="get">get</h3>

docs/site/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -350,7 +350,7 @@ <h3 id="helpers">Helpers</h3>
350350
<tbody>
351351
<tr>
352352
<td><a href="config.html">Config</a></td>
353-
<td>YAML configuration files</td>
353+
<td>Configuration files (TOML, JSON, properties)</td>
354354
</tr>
355355
<tr>
356356
<td><a href="cooldown.html">Cooldown</a></td>

docs/src/config.md

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,42 @@
11
---
22
title: Config
3-
subtitle: YAML configuration files
3+
subtitle: Configuration files (TOML, JSON, properties)
44
---
55

66
# Config
77

8-
`Config` provides a simple interface for reading and writing YAML configuration files. Config files are stored in the plugin's data folder and persist across server restarts.
8+
`Config` provides a simple interface for reading and writing configuration files. Config files are stored in the plugin's data folder and persist across server restarts.
9+
10+
Supported formats: **TOML** (default), **JSON**, and **properties**.
911

1012
---
1113

1214
## Constructor
1315

1416
```python
15-
Config(name=None, defaults=None)
17+
Config(name=None, defaults=None, format="toml")
1618
```
1719

1820
Load or create a configuration file.
1921

2022
- **Parameters:**
21-
- `name` (`str | None`) — File name (without `.yml` extension). If `None`, uses the default `config.yml`.
23+
- `name` (`str | None`) — File name (without extension). If `None`, uses the script name.
2224
- `defaults` (`dict[str, Any] | None`) — Default values to merge into the config if they don't already exist.
25+
- `format` (`str`) — File format: `"toml"` (default), `"json"`, or `"properties"`.
2326

2427
```python
25-
# Default config.yml
28+
# Default config.toml
2629
config = Config()
2730

2831
# Named config
2932
bans = Config("bans")
3033

34+
# JSON format
35+
data = Config("data", format="json")
36+
37+
# Properties format
38+
props = Config("server", format="properties")
39+
3140
# With defaults
3241
settings = Config("settings", defaults={
3342
"spawn_protection_radius": 16,
@@ -50,7 +59,7 @@ The entire config as a dictionary.
5059

5160
- **Type:** `str`
5261

53-
Absolute file path to the YAML file.
62+
Absolute file path to the config file.
5463

5564
---
5665

docs/src/index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ Drop your `.py` file into the server's `scripts/` folder and reload. The bridge
9393

9494
| Page | Description |
9595
|------|-------------|
96-
| [Config](config.md) | YAML configuration files |
96+
| [Config](config.md) | Configuration files (TOML, JSON, properties) |
9797
| [Cooldown](cooldown.md) | Per-player cooldowns |
9898
| [Hologram](hologram.md) | Floating text entities |
9999
| [Menu](menu.md) | Chest GUI builder |

releases/pyjavabridge-dev.jar

210 Bytes
Binary file not shown.

src/main/java/com/pyjavabridge/BridgeInstance.java

Lines changed: 34 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -77,15 +77,23 @@ public class BridgeInstance {
7777

7878
private static final Object UNHANDLED = new Object();
7979

80-
@SuppressWarnings("unused")
81-
private final AtomicInteger requestId = new AtomicInteger(1);
8280
private final Map<Integer, PendingEvent> pendingEvents = new ConcurrentHashMap<>();
8381
private final Object writeLock = new Object();
8482

8583
private final BridgeSerializer serializer;
8684
private final EventDispatcher eventDispatcher;
8785
private final EntitySpawner entitySpawner;
8886

87+
private final ChatFacade chatFacade = new ChatFacade();
88+
private final RaycastFacade raycastFacade = new RaycastFacade();
89+
private final ReflectFacade reflectFacade = new ReflectFacade();
90+
private final RegionFacade regionFacade = new RegionFacade();
91+
private final ParticleFacade particleFacade = new ParticleFacade();
92+
private PermissionsFacade permissionsFacade;
93+
private MetricsFacade metricsFacade;
94+
private RefFacade refFacade;
95+
private CommandsFacade commandsFacade;
96+
8997
private ServerSocket serverSocket;
9098
private Socket socket;
9199
private DataInputStream reader;
@@ -105,6 +113,10 @@ public class BridgeInstance {
105113
this.serializer = new BridgeSerializer(registry, gson, plugin);
106114
this.entitySpawner = new EntitySpawner(plugin.getLogger(), name);
107115
this.eventDispatcher = new EventDispatcher(plugin, serializer, name, pendingEvents, this::send, gson);
116+
this.permissionsFacade = new PermissionsFacade(plugin, permissionAttachments);
117+
this.metricsFacade = new MetricsFacade(plugin);
118+
this.refFacade = new RefFacade(this);
119+
this.commandsFacade = new CommandsFacade(plugin, this);
108120
}
109121

110122
public boolean isRunning() {
@@ -145,10 +157,15 @@ void start() {
145157
bridgeThread.start();
146158
}
147159

160+
private volatile boolean shutdownStarted = false;
161+
148162
void shutdown() {
163+
synchronized (writeLock) {
164+
if (shutdownStarted) return;
165+
shutdownStarted = true;
166+
}
149167
if (Bukkit.isPrimaryThread()) {
150168
new Thread(this::shutdownInternal, "PyJavaBridge-Shutdown-" + name).start();
151-
152169
} else {
153170
shutdownInternal();
154171
}
@@ -216,8 +233,8 @@ private void bridgeLoop() {
216233
} catch (IOException eof) {
217234
break;
218235
}
219-
if (length <= 0 || length > 1_073_741_824) {
220-
logError("Invalid message length: " + length, null);
236+
if (length <= 0 || length > 16_777_216) {
237+
plugin.getLogger().severe("[" + name + "] Invalid message length: " + length);
221238
break;
222239
}
223240
byte[] payload = new byte[length];
@@ -950,15 +967,15 @@ private Object invokeReflective(Object target, String method, List<Object> args)
950967
private Object resolveTarget(String targetName, JsonObject argsObj) throws Exception {
951968
return switch (targetName) {
952969
case "server" -> Bukkit.getServer();
953-
case "chat" -> new ChatFacade();
954-
case "raycast" -> new RaycastFacade();
955-
case "permissions" -> new PermissionsFacade(plugin, permissionAttachments);
956-
case "metrics" -> new MetricsFacade(plugin);
957-
case "ref" -> new RefFacade(this);
958-
case "reflect" -> new ReflectFacade();
959-
case "commands" -> new CommandsFacade(plugin, this);
960-
case "region" -> new RegionFacade();
961-
case "particle" -> new ParticleFacade();
970+
case "chat" -> chatFacade;
971+
case "raycast" -> raycastFacade;
972+
case "permissions" -> permissionsFacade;
973+
case "metrics" -> metricsFacade;
974+
case "ref" -> refFacade;
975+
case "reflect" -> reflectFacade;
976+
case "commands" -> commandsFacade;
977+
case "region" -> regionFacade;
978+
case "particle" -> particleFacade;
962979
default -> throw new IllegalArgumentException("Unknown target: " + targetName);
963980
};
964981
}
@@ -1211,8 +1228,9 @@ private void sendError(int id, String error, Throwable ex, String code) {
12111228
}
12121229

12131230
void logError(String message, Throwable ex) {
1214-
plugin.getLogger().severe("[" + name + "] " + message + ": " + ex.getMessage());
1215-
plugin.broadcastErrorToDebugPlayers("[" + name + "] " + message + ": " + ex.getMessage());
1231+
String detail = ex != null ? ex.getMessage() : "unknown";
1232+
plugin.getLogger().severe("[" + name + "] " + message + ": " + detail);
1233+
plugin.broadcastErrorToDebugPlayers("[" + name + "] " + message + ": " + detail);
12161234
}
12171235

12181236
private void startPythonProcess(int port) {
Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,49 @@
11
package com.pyjavabridge.util;
22

33
import java.util.Collection;
4+
import java.util.IdentityHashMap;
45
import java.util.Map;
56
import java.util.concurrent.ConcurrentHashMap;
67
import java.util.concurrent.atomic.AtomicInteger;
78

89
public class ObjectRegistry {
910
private final Map<Integer, Object> objects = new ConcurrentHashMap<>();
11+
private final IdentityHashMap<Object, Integer> reverseMap = new IdentityHashMap<>();
12+
private final Object reverseLock = new Object();
1013
private final AtomicInteger counter = new AtomicInteger(1);
1114

1215
public int register(Object obj) {
1316
if (obj == null) {
1417
return 0;
1518
}
16-
int id = counter.getAndIncrement();
17-
objects.put(id, obj);
18-
return id;
19+
synchronized (reverseLock) {
20+
Integer existing = reverseMap.get(obj);
21+
if (existing != null && objects.containsKey(existing)) {
22+
return existing;
23+
}
24+
int id = counter.getAndIncrement();
25+
objects.put(id, obj);
26+
reverseMap.put(obj, id);
27+
return id;
28+
}
1929
}
2030

2131
public Object get(int id) {
2232
return objects.get(id);
2333
}
2434

2535
public void release(int id) {
26-
objects.remove(id);
36+
Object removed = objects.remove(id);
37+
if (removed != null) {
38+
synchronized (reverseLock) {
39+
reverseMap.remove(removed);
40+
}
41+
}
2742
}
2843

2944
public void releaseAll(Collection<Integer> ids) {
3045
for (int id : ids) {
31-
objects.remove(id);
46+
release(id);
3247
}
3348
}
3449
}

src/main/resources/plugin.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name: PyJavaBridge
22
main: com.pyjavabridge.PyJavaBridgePlugin
3-
version: 0.1.0
3+
version: '${version}'
44
api: '1.21'
55
api-version: '1.21'
66
author: Omena0

0 commit comments

Comments
 (0)