Emberveil sourcemobile/java/MobileMain.java
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.net.*;
import java.io.*;
import java.nio.file.*;
/** Local browser evaluation entry point. No desktop client or server files are modified. */
public final class MobileMain {
private static MobileGame game;
private static volatile String phase="Not started";
private static volatile boolean signingIn;
private static volatile String[] pendingLogin;
private static final java.util.concurrent.ConcurrentLinkedQueue<int[]> drags=new java.util.concurrent.ConcurrentLinkedQueue<int[]>();
private static final java.util.concurrent.ConcurrentLinkedQueue<int[]> taps=new java.util.concurrent.ConcurrentLinkedQueue<int[]>();
static final class MobileGame extends Game {
volatile boolean ready;
volatile boolean reconnecting;
@Override public void dropClient(){reconnecting=true;try{super.dropClient();}finally{reconnecting=false;}}
int[] activeTap; int tapTicks;
@Override public void processGameLoop(){
String[] request=pendingLogin;
if(request!=null){pendingLogin=null;try{myUsername=request[0];myPassword=request[1];loginScreenState=2;login(request[0],request[1],false);}finally{request[1]="";signingIn=false;}}
if(activeTap==null){activeTap=taps.poll();if(activeTap!=null){movePointer(activeTap[0],activeTap[1]);tapTicks=2;}}
else if(--tapTicks<=0){pressPointer(activeTap[0],activeTap[1],activeTap[2]);activeTap=null;}
for(int count=0;count<12;count++){int[] drag=drags.poll();if(drag==null)break;applyGesture(drag);}
super.processGameLoop();
}
@Override void drawLoadingText(int percent,String text){phase=text;super.drawLoadingText(percent,text);}
@Override public Socket openSocket(int port)throws IOException{return new BrowserSocket(port);}
@Override void startUp(){super.startUp();ready=true;phase="Ready to sign in";}
}
public static synchronized void start(String assetRoot)throws Exception {
if(game!=null)return;
phase="Preparing game files";
String version="2026-10-16-ammunition-v2";
Path cache=Paths.get("/files/emberveil-mobile",version);Files.createDirectories(cache);
String[] files={"main_file_cache.dat","main_file_cache.idx0","main_file_cache.idx1","main_file_cache.idx2","main_file_cache.idx3","main_file_cache.idx4"};
for(String file:files){Path from=Paths.get(assetRoot,file),to=cache.resolve(file);if(!Files.exists(to)||Files.size(to)!=Files.size(from))Files.copy(from,to,StandardCopyOption.REPLACE_EXISTING);}
System.setProperty("emberveil.cache",cache.toString());
System.setProperty("emberveil.browser","true");
ClientSettings.SERVER_IP="127.0.0.1";ClientSettings.SERVER_WORLD=1;ClientSettings.CHECK_CRC=false;
ClientSettings.SHOW_NAVBAR=false;ClientSettings.CUSTOM_SETTINGS_TAB=true;ClientSettings.CONTROL_KEY_ZOOMING=false;
ClientSettings.BILINEAR_MINIMAP_FILTERING=true;ClientSettings.FIX_TRANSPARENCY_OVERFLOW=true;ClientSettings.FULL_512PX_VIEWPORT=true;
ClientSettings.SNOW_FLOOR_ENABLED=false;ClientSettings.SNOW_FLOOR_FORCE_ENABLED=false;ClientSettings.SNOW_OVERLAY_FORCE_ENABLED=false;ClientSettings.SNOW_OVERLAY_ENABLED=false;ClientSettings.HIDE_ROOFS=true;
Game.nodeID=10;Game.portOff=0;Game.setHighMem();Game.isMembers=true;Signlink.storeid=32;Signlink.startpriv(InetAddress.getByName("127.0.0.1"));
phase="Loading Emberveil";game=new MobileGame();game.createClientFrame(503,765);
}
public static String status(){
if(signingIn)return "Connecting";
if(game!=null&&game.reconnecting)return "Reconnecting to Emberveil";
if(game!=null&&game.loggedIn)return "In game";
if(game!=null&&game.ready&&game.loginMessage2!=null&&!game.loginMessage2.isEmpty())return game.loginMessage1+" "+game.loginMessage2;
return phase;
}
public static boolean ready(){return game!=null&&game.ready;}
public static boolean loggedIn(){return game!=null&&game.loggedIn;}
public static boolean busy(){return signingIn||(game!=null&&game.reconnecting);}
public static synchronized void signIn(String username,String password){
if(!ready()||game.loggedIn||busy())return;
if(username==null||password==null||username.length()<1||username.length()>12||password.length()<1||password.length()>20)return;
signingIn=true;pendingLogin=new String[]{username,password};
}
public static void point(int x,int y,int button){
if(!ready()||x<0||y<0||x>=765||y>=503)return;
if(taps.size()<8)taps.add(new int[]{x,y,button});
}
private static int rawX(int x){return (int)Math.round(x*game.displayScale()+(game.getWidth()-765*game.displayScale())/2);}
private static int rawY(int y){return (int)Math.round(y*game.displayScale()+(game.getHeight()-503*game.displayScale())/2);}
private static void movePointer(int x,int y){game.mouseMoved(new MouseEvent(game,MouseEvent.MOUSE_MOVED,System.currentTimeMillis(),0,rawX(x),rawY(y),0,false));}
private static void pressPointer(int x,int y,int button){
int rawX=(int)Math.round(x*game.displayScale()+(game.getWidth()-765*game.displayScale())/2);
int rawY=(int)Math.round(y*game.displayScale()+(game.getHeight()-503*game.displayScale())/2);
int mouseButton=button==2?MouseEvent.BUTTON3:MouseEvent.BUTTON1;
game.mouseMoved(new MouseEvent(game,MouseEvent.MOUSE_MOVED,System.currentTimeMillis(),0,rawX,rawY,0,false));
game.mousePressed(new MouseEvent(game,MouseEvent.MOUSE_PRESSED,System.currentTimeMillis(),0,rawX,rawY,1,button==2,mouseButton));
game.mouseReleased(new MouseEvent(game,MouseEvent.MOUSE_RELEASED,System.currentTimeMillis(),0,rawX,rawY,1,button==2,mouseButton));
}
public static void wheel(int x,int y,int delta){if(ready()&&drags.size()<24)drags.add(new int[]{x,y,0,-delta*30,delta});}
public static void gesture(int x,int y,int dx,int dy){
if(ready()&&drags.size()<24)drags.add(new int[]{x,y,Math.max(-100,Math.min(100,dx)),Math.max(-100,Math.min(100,dy))});
}
private static boolean scrollRoot(int id,int x,int y,int originX,int originY,int delta){
return id>=0&&id<RSInterface.interfaceCache.length&&MobileUi.scroll(RSInterface.interfaceCache[id],originX,originY,x,y,delta,0);
}
private static void applyGesture(int[] d){
if(game.menuOpen)return;int x=d[0],y=d[1],delta=-d[3];
if(x>=0&&y>=0&&x<515&&y<340){
if(game.openInterfaceID!=-1){scrollRoot(game.openInterfaceID,x,y,4,4,delta);game.inputTaken=true;return;}
if(d.length>4){game.changeCameraZoom(d[4]);}
else if(Math.abs(d[2])>=Math.abs(d[3])){game.minimapInt1=(game.minimapInt1+d[2]*3)&2047;game.idleTime=0;}
}else if(x>=553&&y>=205&&y<466){
int id=game.invOverlayInterfaceID!=-1?game.invOverlayInterfaceID:game.tabInterfaceIDs[game.tabID];
if(scrollRoot(id,x,y,553,205,delta))game.needDrawTabArea=true;
}else if(x>=17&&x<496&&y>=357&&y<434&&game.backDialogID==-1){
game.anInt1089=Math.max(0,Math.min(Math.max(0,game.anInt1211-77),game.anInt1089+d[3]));game.inputTaken=true;
}
}
public static void turn(int delta){if(ready()){game.minimapInt1=(game.minimapInt1+delta)&2047;game.idleTime=0;}}
public static void zoom(int delta){if(ready())game.changeCameraZoom(delta);}
public static void type(String text){
if(!ready()||text==null||text.length()>100)return;
for(char c:text.toCharArray())if(c>=32&&c<127)game.keyPressed(new KeyEvent(game,KeyEvent.KEY_PRESSED,System.currentTimeMillis(),0,KeyEvent.VK_UNDEFINED,c));
game.keyPressed(new KeyEvent(game,KeyEvent.KEY_PRESSED,System.currentTimeMillis(),0,KeyEvent.VK_ENTER,'\n'));
}
}