Commits

Don Brown committed 38bbd40

Part way through proxy

- displays in terminal
- buffering problems where underflow-matched strings
don't show to user

  • Participants
  • Parent commits f952843
  • Branches webtokens

Comments (0)

Files changed (11)

File DataLexer.jplex

         <Rule expr="{LD}&#27;\[32mYou have &#27;\[1;33m{THOUSANDS}&#27;\[0;32m fighters" event="fightersAvailable" />
         <Rule expr="{LD}&#27;\[1;36mDone\. You have &#27;\[33m{THOUSANDS}&#27;\[36m fighter\(s\) in close" event="fightersRemain" />
 
+
 		<!-- transporting to ship -->
 		<!-- transport successful -->
 		<!-- you trade ships -->
 		<!-- corpmate trades ships -->
 		<!-- you get blown up - names your escape pod type -->
 		<!-- REMEMBER: changed ship event on all these -->
+
+        <Rule expr="." event="unknown" pri="-100" />
 		
 	</RuleGroup>
 

File src/krum/weaponm/web/MessageSender.java

+package krum.weaponm.web;
+
+/**
+*
+*/
+public interface MessageSender {
+    void send(CharSequence message);
+}

File src/krum/weaponm/web/TerminalWebSocketProtocol.java

  *
  * @author Jeanfrancois Arcand
  */
-public class TerminalWebSocketProtocol implements WebSocketProtocol, BufferObserver {
+public class TerminalWebSocketProtocol implements WebSocketProtocol {
 
     private static final Logger log = LoggerFactory.getLogger(TerminalWebSocketProtocol.class);
-    private volatile Buffer buffer;
-    private volatile NetworkManager networkManager;
-    private volatile WebSocket webSocket;
+    private MessageSender toTwgs;
+    private TwgsProxy twgsProxy;
 
     @Override
     public void configure(AtmosphereConfig config) {
-        buffer = ((Buffer) config.properties().get(Buffer.class.getName()));
-        networkManager = (NetworkManager) config.properties().get(NetworkManager.class.getName());
     }
 
     public final List<AtmosphereRequest> onMessage(WebSocket webSocket, String data) {
-        try {
-            networkManager.write(data);
-        } catch (IOException e) {
-            throw new RuntimeException(e);
-        }
+        toTwgs.send(data);
         return null;
     }
 
      *
      * @param webSocket
      */
-    public void onOpen(WebSocket webSocket) {
+    public void onOpen(final WebSocket webSocket) {
         webSocket.resource().suspend(-1);
-        this.webSocket = webSocket;
-        buffer.addBufferObserver(this);
+        this.twgsProxy = new TwgsProxy();
+        try {
+            toTwgs = twgsProxy.connect(new MessageSender() {
+                @Override
+                public void send(CharSequence message) {
+                    webSocket.resource().getBroadcaster().broadcast(message);
+                }
+            });
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+
     }
 
     /**
      */
     public void onClose(WebSocket webSocket) {
         webSocket.resource().resume();
-        this.webSocket = null;
-        buffer.removeBufferObserver(this);
+        twgsProxy.close();
     }
 
     /**
     public void onError(WebSocket webSocket, WebSocketProcessor.WebSocketException t) {
         log.error(t.getMessage() + " Status {} Message {}", t.response().getStatus(), t.response().getStatusMessage());
     }
+
 //
 //    public void onTextMessage(WebSocket webSocket, String message) {
 //
 //
 //        b.broadcast(new Data(author, chat).toString());
 //    }
-
-
-    @Override
-    public void extentsChanged(Buffer buffer, int x, int y, int width, int height) {
-        if (height < y) {
-            int dy = y - height;
-            StringUtils.repeat("\n", dy);
-            webSocket.resource().getBroadcaster().broadcast(StringUtils.repeat("\n", dy));
-        }
-    }
-
-    @Override
-    public void contentChanged(Buffer buffer, int x, int y, int width, int height) {
-        webSocket.resource().getBroadcaster().broadcast(buffer.getContent(x, y) + ":" +
-                                                        x + ":" +
-                                                        y);
-    }
+//
+//
+//    @Override
+//    public void extentsChanged(Buffer buffer, int x, int y, int width, int height) {
+//        if (height < y) {
+//            int dy = y - height;
+//            StringUtils.repeat("\n", dy);
+//            webSocket.resource().getBroadcaster().broadcast(StringUtils.repeat("\n", dy));
+//        }
+//    }
+//
+//    @Override
+//    public void contentChanged(Buffer buffer, int x, int y, int width, int height) {
+//        webSocket.resource().getBroadcaster().broadcast(buffer.getContent(x, y) + ":" +
+//                                                        x + ":" +
+//                                                        y);
+//    }
 }

File src/krum/weaponm/web/TokenBroadcastingProxy.java

+package krum.weaponm.web;
+
+import krum.jplex.UnderflowException;
+import krum.weaponm.database.lexer.DataEventListener;
+import krum.weaponm.emulation.lexer.EmulationEventListener;
+import krum.weaponm.emulation.lexer.EmulationLexer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
+/**
+ *
+ */
+public class TokenBroadcastingProxy {
+
+    private static final Logger log = LoggerFactory.getLogger(TokenBroadcastingProxy.class);
+
+    public static DataEventListener build(MessageSender broadcaster) {
+        return (DataEventListener) Proxy.newProxyInstance(TokenBroadcastingProxy.class.getClassLoader(), new Class[]{DataEventListener.class}, new TokenBroadcastingInvocationHandler(broadcaster));
+    }
+
+    /**
+     * InvocationHandler for a dynamic proxy that ensures all methods are executed with the object
+     * class's class loader as the context class loader.
+     */
+    private static class TokenBroadcastingInvocationHandler implements InvocationHandler {
+
+        private EmulationLexer emulationLexer;
+        private MessageSender broadcaster;
+
+        TokenBroadcastingInvocationHandler(MessageSender broadcaster) {
+            this.broadcaster = broadcaster;
+            try {
+                this.emulationLexer = new EmulationLexer();
+                this.emulationLexer.addEventListener((EmulationEventListener) Proxy.newProxyInstance(TokenBroadcastingProxy.class.getClassLoader(), new Class[]{EmulationEventListener.class}, new EmulationInvocationHandler(this.broadcaster)));
+            } catch (IOException e) {
+                throw new RuntimeException(e);
+            } catch (ClassNotFoundException e) {
+                throw new RuntimeException(e);
+            }
+        }
+
+        public Object invoke(final Object o, final Method method, final Object[] objects) throws
+                Throwable {
+            String token = method.getName();
+            CharSequence chars = (CharSequence) objects[0];
+            int offset = (Integer) objects[1];
+            int length = (Integer) objects[2];
+            if (!"unknown".equals(token)) {
+                broadcaster.send(token + ": " + chars.subSequence(offset, offset + length));
+            } else {
+                try {
+                    emulationLexer.lex(chars, offset, length, false);
+                } catch (UnderflowException ignore) {}
+            }
+            return null;
+        }
+
+        private static class EmulationInvocationHandler implements InvocationHandler {
+            private final MessageSender broadcaster;
+
+            private EmulationInvocationHandler(MessageSender broadcaster) {
+                this.broadcaster = broadcaster;
+            }
+
+            @Override
+            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+                CharSequence chars = (CharSequence) args[0];
+                int offset = (Integer) args[1];
+                int length = (Integer) args[2];
+                broadcaster.send(chars.subSequence(offset, offset + length));
+                return null;
+            }
+        }
+    }
+
+}

File src/krum/weaponm/web/TwgsProxy.java

+package krum.weaponm.web;
+
+import krum.automaton.TokenResult;
+import krum.jplex.UnderflowException;
+import krum.weaponm.WeaponM;
+import krum.weaponm.database.lexer.DataEventListener;
+import krum.weaponm.database.lexer.DataLexer;
+import krum.weaponm.database.lexer.DataState;
+import krum.weaponm.network.AnsiConverter;
+
+import java.io.*;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.nio.CharBuffer;
+
+/**
+ *
+ */
+public class TwgsProxy {
+    private Socket toTwgs;
+
+//    public static void main(String[] args) throws IOException {
+//
+//        WeaponHttpServer server = new WeaponHttpServer();
+//
+//        ServerSocket serverSocket = new ServerSocket(8888);
+//        while (true) {
+//            final Socket toClient = serverSocket.accept();
+//            final Socket toTwgs = new Socket("192.168.0.10", 23);
+//
+//
+//            new Thread(new Runnable() {
+//                @Override
+//                public void run() {
+//                    try {
+//                        Feeder clientToTwgs = new Feeder(toClient.getInputStream(), toTwgs.getOutputStream());
+//                        Feeder twgsToClient = new Feeder(toTwgs.getInputStream(), new LexingOutputStream(toClient.getOutputStream()));
+//
+//                        clientToTwgs.start();
+//                        twgsToClient.start();
+//
+//                        twgsToClient.join();
+//                        closeSockets(toClient, toTwgs);
+//
+//                    } catch (IOException e) {
+//                        closeSockets(toClient, toTwgs);
+//                    } catch (RuntimeException e) {
+//                        closeSockets(toClient, toTwgs);
+//                    } catch (InterruptedException ignore) {
+//                        closeSockets(toClient, toTwgs);
+//                    }
+//                }
+//            }).start();
+//        }
+//    }
+
+    public MessageSender connect(final MessageSender sendToClient) throws IOException {
+        toTwgs = new Socket("192.168.0.10", 23);
+        toTwgs.getOutputStream().write(new byte[]{(byte)0xFF, (byte)0xFC, (byte)0xF6});
+        toTwgs.getOutputStream().flush();
+
+        final MessageSender sendToTwgs = new MessageSender() {
+            @Override
+            public void send(CharSequence message) {
+                message = "\r".equals(message) ? "\r\n" : message;
+                try {
+                    toTwgs.getOutputStream().write(message.toString().getBytes());
+                } catch (IOException e) {
+                    throw new RuntimeException(e);
+                }
+            }
+        };
+        new Thread(new Runnable() {
+            @Override
+            public void run() {
+                try {
+                    Feeder twgsToClient = new Feeder(toTwgs.getInputStream(), sendToClient);
+
+                    twgsToClient.start();
+
+                    twgsToClient.join();
+                    closeSockets(toTwgs);
+
+                } catch (IOException e) {
+                    closeSockets(toTwgs);
+                } catch (RuntimeException e) {
+                    closeSockets(toTwgs);
+                } catch (InterruptedException ignore) {
+                    closeSockets(toTwgs);
+                }
+            }
+        }).start();
+        return sendToTwgs;
+    }
+
+    private static void closeSockets(Socket... sockets) {
+        for (Socket socket : sockets) {
+            try {
+                if (socket != null && socket.isConnected()) {
+                    socket.close();
+                }
+            } catch (IOException ignore) {
+                ignore.printStackTrace();
+            }
+        }
+    }
+
+    public void close() {
+        closeSockets(toTwgs);
+    }
+
+    private static class NumericLexer extends DataLexer {
+
+        private NumericLexer() throws IOException, ClassNotFoundException {
+            super();
+        }
+
+        public int lexNoIndex(CharSequence seq, int off, int len, boolean endOfInput) {
+            if(off < 0 || len < 0 || off + len > seq.length()) throw new IndexOutOfBoundsException();
+            int index = off;
+            while(index < off + len) {
+                if(automaton.find(seq, index, endOfInput, token)) {
+                    if(token.info != null) dispatch(token);
+                    index += token.len;
+                }
+                else if(token.info == TokenResult.UNDERFLOW) {
+                    if(index == 0) return -1; // throw new UnderflowException();
+                    else return index;
+                }
+                else ++index;
+            }
+            return index;
+        }
+    }
+
+    public static class Feeder extends Thread {
+
+        private final InputStream in;
+        private final CharBuffer buffer;
+        private final NumericLexer lexer;
+
+
+        public Feeder(InputStream in, MessageSender out) {
+            this.in = new BufferedInputStream(in);
+            this.buffer = CharBuffer.allocate(4096);
+            try {
+                this.lexer = new NumericLexer();
+            } catch (IOException e) {
+                throw new RuntimeException(e);
+            } catch (ClassNotFoundException e) {
+                throw new RuntimeException(e);
+            }
+            lexer.jumpState(DataState.CORRECT_GAME);
+            DataEventListener listener = TokenBroadcastingProxy.build(out);
+            lexer.addEventListener(listener);
+        }
+
+        @Override
+        public void run() {
+            try {
+                byte[] byteBuffer = new byte[4096];
+                int len;
+                while (true) {
+                    len = in.read(byteBuffer);
+                    if (len > 0) {
+                        for (int x = 0; x < len; x++) {
+                            buffer.put((char) (byteBuffer[x] & 0xFF));
+                        }
+                        buffer.flip();
+                        int pos = buffer.position();
+                        while(buffer.hasRemaining()) {
+                            //pos += emulation.write(emulationBuffer, 0, emulationBuffer.length(), false);
+                            int written = lexer.lexNoIndex(buffer, 0, buffer.length(), false);
+                            if (written == -1) {
+                                break;
+                            } else {
+                                pos += written;
+                                buffer.position(pos);
+                            }
+                        }
+                        buffer.compact();
+                    } else {
+                        break;
+                    }
+                }
+            } catch (IOException ignore) {
+                ignore.printStackTrace();
+            }
+        }
+    }
+}

File src/krum/weaponm/web/WeaponHttpServer.java

 import org.atmosphere.cpr.AtmosphereServlet;
 import org.atmosphere.interceptor.AtmosphereResourceLifecycleInterceptor;
 import org.atmosphere.interceptor.BroadcastOnPostAtmosphereInterceptor;
+import org.atmosphere.websocket.WebSocketProtocol;
 import org.eclipse.jetty.server.Handler;
 import org.eclipse.jetty.server.Server;
 import org.eclipse.jetty.server.handler.DefaultHandler;
 
     private final Server server;
 
-    public WeaponHttpServer(Buffer buffer, NetworkManager networkManager) {
+    public WeaponHttpServer() {
         server = new Server(8080);
 
         ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
 //        context.setInitParameter("org.atmosphere.websocket.binaryWrite", "true");
         final AtmosphereServlet servlet = new AtmosphereServlet();
         List<AtmosphereInterceptor> l = Arrays.<AtmosphereInterceptor>asList(new AtmosphereResourceLifecycleInterceptor(), new BroadcastOnPostAtmosphereInterceptor());
-        servlet.framework().getAtmosphereConfig().properties().put(Buffer.class.getName(), buffer);
-        servlet.framework().getAtmosphereConfig().properties().put(NetworkManager.class.getName(), networkManager);
+//        servlet.framework().getAtmosphereConfig().properties().put(Buffer.class.getName(), nu);
+//        servlet.framework().getAtmosphereConfig().properties().put(NetworkManager.class.getName(), networkManager);
         servlet.framework().setWebSocketProtocolClassName(TerminalWebSocketProtocol.class.getName());
         context.addServlet(new ServletHolder(servlet), "/terminal/*");
 
         HandlerList handlers = new HandlerList();
         handlers.setHandlers(new Handler[]{context, resource_handler, new DefaultHandler()});
         server.setHandler(handlers);
+
     }
 
     public void start() {
         try {
             server.start();
+            server.join();
         } catch (Exception e) {
             throw new RuntimeException(e);
         }
         }
     }
 
+    public static void main(String[] args) {
+        new WeaponHttpServer().start();
+    }
+
 }

File src/web/index.html

     <div id="terminal">
 </div>
 
+<input id="input" type="text" size="50"/>
+
 
 
 </body>

File src/web/js/application.js

         var message = response.responseBody;
 
         var split = message.split(":");
-        if (split.length == 1 && split[0].indexOf("\n") == 0) {
-            terminal.advance(split[0].length);
+        if (split.length == 1) {
+            console.log("single message: [" + message.charCodeAt(0) + "]");
+            terminal.parse(message);
         } else {
-            var charWithAttributes = parseInt(split[0]);
-            var x = parseInt(split[1]);
-            var y = parseInt(split[2]);
-            terminal.write(x, y, charWithAttributes);
+            console.log("token message: [" + message + "]");
+            var token = split[0];
+            var data = split[1];
+            console.log("token: " + token);
+            terminal.parse(data);
         }
     };
 
 
     subSocket = socket.subscribe(request);
 
-    $('#terminal').keydown(function(e) {
+    $('#input').keydown(function(e) {
         console.log("sending " + String.fromCharCode(e.which));
         subSocket.push(String.fromCharCode(e.which));
     });

File src/web/js/escapes.js

                 options.onLiteral.call(this, buffer.slice(pos));
             }
 
-            this.trimCanvas();
+            //this.trimCanvas();
             options.onComplete.call(this.canvas, this);
             return this;
         },

File src/web/js/jquery.atmosphere.js

                     if (messages.length > 1 && messages[i].length == 0) {
                         continue;
                     }
-                    _response.responseBody = jQuery.trim(messages[i]);
+
+                    // FIXME: why would it trim here?
+//                    _response.responseBody = jQuery.trim(messages[i]);
+                    _response.responseBody = messages[i];
 
                     // Ugly see issue 400.
                     if (_response.responseBody.length == 0 && _response.transport == 'streaming' && _response.state == "messageReceived") {

File src/web/js/term.js

             term.write(text);
         }
 
+        var noop = function() {};
+        this.parse = function(data) {
+            term.parse(data, {
+                            onEscape    : term.escape,
+                            onLiteral   : term.write,
+                            onComplete  : noop
+                        });
+        }
+
         this.attach = function(div) {
             div.append(term.canvas);
         }