package oculusPrime; import oculusPrime.commport.ArduinoPrime; import org.red5.client.net.rtmp.BaseRTMPClientHandler; import org.red5.client.net.rtmp.ClientExceptionHandler; import org.red5.client.net.rtmp.INetStreamEventHandler; import org.red5.client.net.rtmp.RTMPClient; import org.red5.io.utils.ObjectMap; import org.red5.server.api.IConnection; import org.red5.server.api.event.IEvent; import org.red5.server.api.event.IEventDispatcher; import org.red5.server.api.service.IPendingServiceCall; import org.red5.server.api.service.IPendingServiceCallback; import org.red5.server.api.service.IServiceCapableConnection; import org.red5.server.net.rtmp.Channel; import org.red5.server.net.rtmp.RTMPConnection; import org.red5.server.net.rtmp.event.Notify; import org.red5.server.net.rtmp.event.Ping; import org.red5.server.net.rtmp.message.Header; import org.red5.server.net.rtmp.status.StatusCodes; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.Collection; import java.util.Map; import java.util.Set; public class Red5Client extends RTMPClient { private State state = State.getReference(); private Settings settings = Settings.getReference(); private Application app; private NetworkServlet networkServlet = new NetworkServlet(); private long lastping; private static final long PINGTIMEOUT = 10000; private long stayConnectedId; public Red5Client(Application a) { app = a; // this.setConnectionClosedHandler(new Runnable() { // @Override // public void run() { // // Util.log("relay server connection closed", this); // state.delete(State.values.relayserver); // } // }); this.setServiceProvider(this); } public void connectToRelay() { connectToRelay(null); } public void connectToRelay(String str) { String[] hostuserpass = null; if (str != null) { hostuserpass = str.split(" "); if (hostuserpass.length != 3) { app.driverCallServer(PlayerCommands.messageclients, "invalid relay server login info"); return; } settings.writeSettings(GUISettings.relayserver, hostuserpass[0]); settings.writeSettings(GUISettings.relayserverauth, Settings.DISABLED); // Util.log(hostuserpass[0]+hostuserpass[1]+hostuserpass[2], this); } if (settings.getBoolean(ManualSettings.useflash)) { state.set(State.values.guinotify, "relay server in use, setting "useflash" set to false"); Util.log("setting useflash false", this); settings.writeSettings(ManualSettings.useflash, Settings.FALSE); } if (state.exists(State.values.relayserver)) { app.driverCallServer(PlayerCommands.messageclients, "relay server already connected to: " + state.get(State.values.relayserver)); return; } if (hostuserpass == null && (settings.readSetting(GUISettings.relayserver).equals(Settings.DISABLED) || settings.readSetting(GUISettings.relayserverauth).equals(Settings.DISABLED)) ) { app.driverCallServer(PlayerCommands.messageclients, "relay server not set"); return; } setExceptionHandler(new ClientExceptionHandler() { @Override public void handleException(Throwable throwable) { Util.log("connectToRelay(): " + throwable.getLocalizedMessage(), this); // throwable.printStackTrace(); } }); String server = settings.readSetting(GUISettings.relayserver); String app = "oculusPrime"; int port = Integer.valueOf(settings.readRed5Setting("rtmp.port")); String args[] = new String[1]; if (hostuserpass !=null) { args[0] = hostuserpass[1]+" "+hostuserpass[2]+" remember"; } else { args[0] = settings.readSetting(GUISettings.relayserverauth); } connect(server, port, makeDefaultConnectionParams(server, port, app), connectCallback, args); stayConnected(); } private IPendingServiceCallback connectCallback = new IPendingServiceCallback() { @Override public void resultReceived(IPendingServiceCall call) { ObjectMap<?, ?> map = (ObjectMap<?, ?>) call.getResult(); String code = (String) map.get("code"); Util.log("Red5Client connectCallback Response code: " + code, this); if ("NetConnection.Connect.Success".equals(code)) { // success Util.log("Remote relay server connect success", this); if (state.exists(State.values.relayserver)) { // just in case Util.log("error, state relay server exists", this); return; } if (state.exists(State.values.driver)) { app.driverCallServer(PlayerCommands.messageclients, "connected to relay server, logging out this connection"); app.driverCallServer(PlayerCommands.driverexit, null); } state.set(State.values.relayserver, settings.readSetting(GUISettings.relayserver)); app.driverCallServer(PlayerCommands.messageclients, "connected to relay server: " + state.get(State.values.relayserver)); // notify remote server that this is an authenticated relay client invoke("setRelayClient", new IPendingServiceCallback() { @Override public void resultReceived(IPendingServiceCall iPendingServiceCall) { } }); } else if ("NetConnection.Connect.Rejected".equals(code)) { disconnect(); Util.log("Red5Client connect rejected: " + map.get("description"), this); state.delete(State.values.relayserver); } else { Util.log("Remote relay server connect failed", this); disconnect(); state.delete(State.values.relayserver); } } }; // stay connected as long as relayserver setting set private void stayConnected() { new Thread(new Runnable() { public void run() { try { long id = System.currentTimeMillis(); stayConnectedId = id; lastping = id; Util.delay(5000); // allow time to connect while(!settings.readSetting(GUISettings.relayserver).equals(Settings.DISABLED) && stayConnectedId == id) { if (System.currentTimeMillis() - lastping < PINGTIMEOUT && state.exists(State.values.relayserver)) { // all is well // ping server Util.debug("ping server", this); // TODO: testing invoke("relayPing", new IPendingServiceCallback() { @Override public void resultReceived(IPendingServiceCall iPendingServiceCall) { } }); Util.delay(1000); continue; } if (stayConnectedId != id) break; // all is not well if (!state.exists(State.values.relayserver)) { Util.log("state relayserver null", this); } if (System.currentTimeMillis() - lastping > PINGTIMEOUT) { Util.log("ping timeout", this); } app.driverCallServer(PlayerCommands.publish, Application.streamstate.stop.toString()); state.delete(State.values.relayserver); Util.delay(10000); if (stayConnectedId != id) break; Util.log("attempting reconnect", this); // TODO: testing connectToRelay(); } Util.debug("stayConnected Thread exit", this); // TODO: testing } catch (Exception e) { Util.printError(e); } } }).start(); } public void sendToRelay(String functionName, Object[] params) { Object[] functionplusparams = new Object[params.length+1]; functionplusparams[0]=functionName; for (int i=1; i<functionplusparams.length; i++) functionplusparams[i]=params[i-1]; invoke("fromRelayClient", functionplusparams, new IPendingServiceCallback() { @Override public void resultReceived(IPendingServiceCall iPendingServiceCall) { } }); } public void relayDisconnect() { state.delete(State.values.relayserver); disconnect(); } // called by relay server only public void relayPong() { lastping = System.currentTimeMillis(); } // called by relay server only public void relayCallClient(Object[] params) { String str = null; if (params[1]!=null) str=params[1].toString(); app.driverCallServer(PlayerCommands.valueOf(params[0].toString()), str); } // called by relay server only public void playerSignIn(Object[] params) { // disonnect any drivers/passengers/pending boolean delay = false; Collection<Set<IConnection>> concollection = app.getConnections(); for (Set<IConnection> cc : concollection) { for (IConnection con : cc) { if (con instanceof IServiceCapableConnection && con != app.grabber) { con.close(); delay = true; } } } if (delay) Util.delay(500); // allow player to logout TODO: blocking! state.set(State.values.driver, params[0].toString()); app.initialstatuscalled = false; Util.log("relay playersignin(): " + params[0].toString(), this); app.loginRecords.beDriver(); if (settings.getBoolean(GUISettings.loginnotify)) app.driverCallServer(PlayerCommands.speech, state.get(State.values.driver)); app.watchdog.lastpowererrornotify = null; } // called by relay server only public void playerDisconnect() { app.loginRecords.signoutDriver(); //if autodocking, keep autodocking if (!state.getBoolean(State.values.autodocking) && !(state.exists(State.values.navigationroute) && !state.exists(State.values.nextroutetime)) ) { if (!state.get(State.values.driverstream).equals(Application.driverstreamstate.pending.toString())) { if (state.get(State.values.stream) != null) { if (!state.get(State.values.stream).equals(Application.streamstate.stop.toString())) { app.driverCallServer(PlayerCommands.publish, Application.streamstate.stop.toString()); } } app.driverCallServer(PlayerCommands.spotlight, "0"); app.driverCallServer(PlayerCommands.floodlight, "0"); app.driverCallServer(PlayerCommands.move, ArduinoPrime.direction.stop.toString()); } } } }