Run applets that use LiveConnect or load images through the proxy

appletviewer has no netscape.javascript.JSObject, so applets that touch
JavaScript fail to load, and Java 8 denies applets the URLPermission they
need to fetch through an HTTP proxy. Add a JSObject stand-in with a working
document.cookie and a policy granting HTTP URL access.
This commit is contained in:
2026-10-11 00:07:06 -07:00
parent b0751fdbb1
commit dca48bce51
7 changed files with 117 additions and 0 deletions
+7
View File
@@ -1,3 +1,9 @@
FROM eclipse-temurin:8-jdk AS jsobject
COPY jsobject /src
RUN mkdir /classes && \
javac -d /classes $(find /src -name '*.java') && \
jar cf /jsobject.jar -C /classes .
FROM ghcr.io/linuxserver/baseimage-selkies:debiantrixie FROM ghcr.io/linuxserver/baseimage-selkies:debiantrixie
RUN apt-get update && \ RUN apt-get update && \
@@ -12,3 +18,4 @@ RUN apt-get update && \
rm -rf /var/lib/apt/lists/* rm -rf /var/lib/apt/lists/*
COPY root/ / COPY root/ /
COPY --from=jsobject /jsobject.jar /opt/flashpoint/jsobject.jar
+5
View File
@@ -33,3 +33,8 @@ different game, set `GAME_ID` and recreate the container. The launch log is at
the game server, mounts the game's zip through the server's `mountzip` API, and the game server, mounts the game's zip through the server's `mountzip` API, and
runs the game's application with its HTTP proxy pointed at the game server. runs the game's application with its HTTP proxy pointed at the game server.
Selkies restarts the game when it exits (`RESTART_APP`). Selkies restarts the game when it exits (`RESTART_APP`).
`appletviewer` lacks two things applets got from the browser's Java plugin, so
the image adds them: a `netscape.javascript.JSObject` stand-in (`jsobject/`)
whose window has a working `document.cookie`, and a policy allowing HTTP
requests through the game server's proxy.
@@ -0,0 +1,37 @@
package netscape.javascript;
import java.util.LinkedHashMap;
import java.util.Map;
/** A document whose cookie member behaves like document.cookie in a browser. */
class Document extends JSObject {
private final Map<String, String> cookies = new LinkedHashMap<String, String>();
@Override
public Object getMember(String name) throws JSException {
if (!name.equals("cookie")) {
return super.getMember(name);
}
StringBuilder sb = new StringBuilder();
for (Map.Entry<String, String> cookie : cookies.entrySet()) {
if (sb.length() > 0) {
sb.append("; ");
}
sb.append(cookie.getKey()).append('=').append(cookie.getValue());
}
return sb.toString();
}
@Override
public void setMember(String name, Object value) throws JSException {
if (!name.equals("cookie")) {
super.setMember(name, value);
return;
}
String pair = String.valueOf(value).split(";", 2)[0];
int eq = pair.indexOf('=');
if (eq > 0) {
cookies.put(pair.substring(0, eq).trim(), pair.substring(eq + 1).trim());
}
}
}
@@ -0,0 +1,10 @@
package netscape.javascript;
public class JSException extends RuntimeException {
public JSException() {
}
public JSException(String message) {
super(message);
}
}
@@ -0,0 +1,53 @@
package netscape.javascript;
import java.applet.Applet;
import java.util.HashMap;
import java.util.Map;
/**
* Stand-in for the browser's LiveConnect bridge, which appletviewer lacks. The
* window holds a document with a working cookie; calls into JavaScript do nothing.
*/
public class JSObject {
private static JSObject window;
private final Map<String, Object> members = new HashMap<String, Object>();
protected JSObject() {
}
public static synchronized JSObject getWindow(Applet applet) throws JSException {
if (window == null) {
window = new JSObject();
window.setMember("document", new Document());
}
return window;
}
public Object call(String methodName, Object[] args) throws JSException {
return null;
}
public Object eval(String s) throws JSException {
return null;
}
public Object getMember(String name) throws JSException {
return members.get(name);
}
public void setMember(String name, Object value) throws JSException {
members.put(name, value);
}
public void removeMember(String name) throws JSException {
members.remove(name);
}
public Object getSlot(int index) throws JSException {
return null;
}
public void setSlot(int index, Object value) throws JSException {
}
}
@@ -0,0 +1,3 @@
grant {
permission java.net.URLPermission "http:*", "*:*";
};
@@ -34,6 +34,8 @@ fi
echo "Launching $ID: $app $cmd" echo "Launching $ID: $app $cmd"
case ${app,,} in case ${app,,} in
'fpsoftware\startjava.bat' | 'fpsoftware\startjavainbrowser.bat') 'fpsoftware\startjava.bat' | 'fpsoftware\startjavainbrowser.bat')
# Applets expect the browser's JavaScript bridge and a browser-like network policy
export JAVA_TOOL_OPTIONS="-Xbootclasspath/a:/opt/flashpoint/jsobject.jar -Djava.security.policy=/opt/flashpoint/applet.policy"
FP_PATH=$FP_PATH exec "$FP_PATH/FPSoftware/startJava.sh" "$cmd" ;; FP_PATH=$FP_PATH exec "$FP_PATH/FPSoftware/startJava.sh" "$cmd" ;;
*) *)
echo "Unsupported application: $app" >&2; exit 1 ;; echo "Unsupported application: $app" >&2; exit 1 ;;