diff --git a/pixelwar/.settings/org.eclipse.jdt.ui.prefs b/pixelwar/.settings/org.eclipse.jdt.ui.prefs
index 1e757e8..6b47616 100644
--- a/pixelwar/.settings/org.eclipse.jdt.ui.prefs
+++ b/pixelwar/.settings/org.eclipse.jdt.ui.prefs
@@ -13,12 +13,14 @@ sp_cleanup.always_use_blocks=true
sp_cleanup.always_use_parentheses_in_expressions=false
sp_cleanup.always_use_this_for_non_static_field_access=false
sp_cleanup.always_use_this_for_non_static_method_access=false
-sp_cleanup.convert_to_enhanced_for_loop=false
+sp_cleanup.convert_functional_interfaces=false
+sp_cleanup.convert_to_enhanced_for_loop=true
sp_cleanup.correct_indentation=false
sp_cleanup.format_source_code=true
sp_cleanup.format_source_code_changes_only=false
-sp_cleanup.make_local_variable_final=false
-sp_cleanup.make_parameters_final=false
+sp_cleanup.insert_inferred_type_arguments=false
+sp_cleanup.make_local_variable_final=true
+sp_cleanup.make_parameters_final=true
sp_cleanup.make_private_fields_final=true
sp_cleanup.make_type_abstract_if_missing_method=false
sp_cleanup.make_variable_declarations_final=true
@@ -29,15 +31,16 @@ sp_cleanup.organize_imports=true
sp_cleanup.qualify_static_field_accesses_with_declaring_class=false
sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
-sp_cleanup.qualify_static_member_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_member_accesses_with_declaring_class=true
sp_cleanup.qualify_static_method_accesses_with_declaring_class=false
sp_cleanup.remove_private_constructors=true
-sp_cleanup.remove_trailing_whitespaces=false
+sp_cleanup.remove_redundant_type_arguments=false
+sp_cleanup.remove_trailing_whitespaces=true
sp_cleanup.remove_trailing_whitespaces_all=true
sp_cleanup.remove_trailing_whitespaces_ignore_empty=false
sp_cleanup.remove_unnecessary_casts=true
sp_cleanup.remove_unnecessary_nls_tags=false
-sp_cleanup.remove_unused_imports=false
+sp_cleanup.remove_unused_imports=true
sp_cleanup.remove_unused_local_variables=false
sp_cleanup.remove_unused_private_fields=true
sp_cleanup.remove_unused_private_members=false
@@ -45,10 +48,13 @@ sp_cleanup.remove_unused_private_methods=true
sp_cleanup.remove_unused_private_types=true
sp_cleanup.sort_members=false
sp_cleanup.sort_members_all=false
-sp_cleanup.use_blocks=false
+sp_cleanup.use_anonymous_class_creation=false
+sp_cleanup.use_blocks=true
sp_cleanup.use_blocks_only_for_return_and_throw=false
+sp_cleanup.use_lambda=false
sp_cleanup.use_parentheses_in_expressions=false
-sp_cleanup.use_this_for_non_static_field_access=false
+sp_cleanup.use_this_for_non_static_field_access=true
sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true
-sp_cleanup.use_this_for_non_static_method_access=false
+sp_cleanup.use_this_for_non_static_method_access=true
sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true
+sp_cleanup.use_type_arguments=false
diff --git a/pixelwar/pom.xml b/pixelwar/pom.xml
index ec7c128..f9ea782 100644
--- a/pixelwar/pom.xml
+++ b/pixelwar/pom.xml
@@ -56,5 +56,10 @@
netty-all
4.0.18.Final
+
+ org.apache.commons
+ commons-lang3
+ 3.3.2
+
\ No newline at end of file
diff --git a/pixelwar/src/de/paws/pixelwar/NetCanvas.java b/pixelwar/src/de/paws/pixelwar/NetCanvas.java
index e6a7f92..bed6e5f 100644
--- a/pixelwar/src/de/paws/pixelwar/NetCanvas.java
+++ b/pixelwar/src/de/paws/pixelwar/NetCanvas.java
@@ -8,69 +8,42 @@ import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Transparency;
import java.awt.event.ComponentEvent;
+import java.awt.event.ComponentListener;
import java.awt.event.KeyEvent;
+import java.awt.event.KeyListener;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;
import javax.swing.JFrame;
-public class NetCanvas {
+public class NetCanvas implements ComponentListener, KeyListener {
private volatile BufferedImage drawBuffer;
private volatile BufferedImage overlayBuffer;
private volatile BufferedImage paintBuffer;
- private final BufferStrategy strategy;
+ private BufferStrategy strategy;
private final Thread refresher;
private final JFrame frame;
private final Canvas canvas;
public NetCanvas() {
-
- frame = new JFrame() {
- private static final long serialVersionUID = 1L;
-
- @Override
- protected void processKeyEvent(KeyEvent e) {
- super.processKeyEvent(e);
- if (e.getKeyChar() == 'c') {
- Graphics g = drawBuffer.getGraphics();
- g.setColor(Color.BLACK);
- g.fillRect(0, 0, drawBuffer.getWidth(),
- drawBuffer.getHeight());
- g.dispose();
- }
- if (e.getKeyChar() == 'q'
- || e.getKeyCode() == KeyEvent.VK_ESCAPE) {
- System.exit(0);
- }
- }
-
- @Override
- protected void processComponentEvent(ComponentEvent e) {
- super.processComponentEvent(e);
- if (e.getID() == ComponentEvent.COMPONENT_RESIZED) {
- resizeBuffer(getWidth(), getHeight());
- }
- }
- };
-
+ frame = new JFrame();
canvas = new Canvas();
- canvas.setSize(800, 600);
+ resizeBuffer(1024, 1024);
+ frame.addComponentListener(this);
+ canvas.addKeyListener(this);
+
+ canvas.setSize(800, 600);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setTitle("Pixelflut");
// frame.setUndecorated(true);
frame.setResizable(true);
// frame.setAlwaysOnTop(true);
-
frame.add(canvas);
frame.pack();
frame.setVisible(true);
- canvas.createBufferStrategy(2);
- strategy = canvas.getBufferStrategy();
- resizeBuffer(1024, 1024);
-
refresher = new Thread(new Runnable() {
@Override
public void run() {
@@ -79,34 +52,37 @@ public class NetCanvas {
draw();
Thread.sleep(1000 / 30);
}
- } catch (InterruptedException e) {
+ } catch (final InterruptedException e) {
}
}
});
refresher.start();
+
}
- private void blit(BufferedImage source, BufferedImage target) {
- Graphics g = target.getGraphics();
+ private void blit(final BufferedImage source, final BufferedImage target) {
+ final Graphics g = target.getGraphics();
g.drawImage(source, 0, 0, null);
g.dispose();
}
- synchronized private void resizeBuffer(int w, int h) {
+ synchronized private void resizeBuffer(final int w, final int h) {
BufferedImage newBuffer;
int mw = w, mh = h;
if (drawBuffer != null) {
mw = Math.max(w, drawBuffer.getWidth());
mh = Math.max(h, drawBuffer.getHeight());
- if (mw > w && mh > h)
+ if (mw > w && mh > h) {
return;
+ }
}
newBuffer = frame.getGraphicsConfiguration().createCompatibleImage(w,
h, Transparency.OPAQUE);
- if (drawBuffer != null)
+ if (drawBuffer != null) {
blit(drawBuffer, newBuffer);
+ }
drawBuffer = newBuffer;
overlayBuffer = frame.getGraphicsConfiguration().createCompatibleImage(
@@ -117,7 +93,7 @@ public class NetCanvas {
}
private void drawOverlay() {
- Graphics2D g = overlayBuffer.createGraphics();
+ final Graphics2D g = overlayBuffer.createGraphics();
g.setComposite(AlphaComposite.Clear);
g.fillRect(0, 0, overlayBuffer.getWidth(), overlayBuffer.getHeight());
g.setComposite(AlphaComposite.SrcOver);
@@ -128,34 +104,38 @@ public class NetCanvas {
}
synchronized public void draw() {
+ if (!frame.isVisible()) {
+ return;
+ }
drawOverlay();
// Prepare composite buffer
- Graphics2D g = paintBuffer.createGraphics();
+ final Graphics2D g = paintBuffer.createGraphics();
g.drawImage(drawBuffer, 0, 0, null);
g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER));
g.drawImage(overlayBuffer, 0, 0, null);
g.dispose();
// Blit into double buffer
- Graphics g2 = strategy.getDrawGraphics();
+ final Graphics g2 = strategy.getDrawGraphics();
g2.drawImage(paintBuffer, 0, 0, null);
g2.dispose();
strategy.show();
}
- public void setPixel(int x, int y, int argb) {
+ public void setPixel(final int x, final int y, final int argb) {
// Below this values 8bit color channels are nulled.
- BufferedImage img = drawBuffer;
+ final BufferedImage img = drawBuffer;
if (x >= 0 && x < img.getWidth() && y >= 0 && y < img.getHeight()) {
- int alpha = (argb >>> 24) % 256;
+ final int alpha = (argb >>> 24) % 256;
int rgb = argb & 0xffffff;
- if (alpha < 1)
+ if (alpha < 1) {
return;
+ }
if (alpha < 255) {
- int target = img.getRGB(x, y);
+ final int target = img.getRGB(x, y);
int r, g, b;
r = ((target >>> 16) & 0xff) * (255 - alpha);
@@ -176,10 +156,11 @@ public class NetCanvas {
}
}
- public int getPixel(int x, int y) {
- BufferedImage img = drawBuffer;
- if (x >= 0 && x < img.getWidth() && y >= 0 && y < img.getHeight())
+ public int getPixel(final int x, final int y) {
+ final BufferedImage img = drawBuffer;
+ if (x >= 0 && x < img.getWidth() && y >= 0 && y < img.getHeight()) {
return img.getRGB(x, y) & 0x00ffffff;
+ }
return 0;
}
@@ -191,4 +172,44 @@ public class NetCanvas {
return drawBuffer.getHeight();
}
+ @Override
+ public void keyReleased(final KeyEvent e) {
+ if (e.getKeyChar() == 'c') {
+ final Graphics g = drawBuffer.getGraphics();
+ g.setColor(Color.BLACK);
+ g.fillRect(0, 0, drawBuffer.getWidth(), drawBuffer.getHeight());
+ g.dispose();
+ } else if (e.getKeyChar() == 'q'
+ || e.getKeyCode() == KeyEvent.VK_ESCAPE) {
+ System.exit(0);
+ }
+ }
+
+ @Override
+ public void keyPressed(final KeyEvent e) {
+ }
+
+ @Override
+ public void keyTyped(final KeyEvent e) {
+ }
+
+ @Override
+ public void componentResized(final ComponentEvent e) {
+ resizeBuffer(canvas.getWidth(), canvas.getHeight());
+ }
+
+ @Override
+ public void componentMoved(final ComponentEvent e) {
+ }
+
+ @Override
+ public void componentShown(final ComponentEvent e) {
+ canvas.createBufferStrategy(2);
+ strategy = canvas.getBufferStrategy();
+ }
+
+ @Override
+ public void componentHidden(final ComponentEvent e) {
+ }
+
}
diff --git a/pixelwar/src/de/paws/pixelwar/PixelClientHandler.java b/pixelwar/src/de/paws/pixelwar/PixelClientHandler.java
index 0198365..a2399fa 100644
--- a/pixelwar/src/de/paws/pixelwar/PixelClientHandler.java
+++ b/pixelwar/src/de/paws/pixelwar/PixelClientHandler.java
@@ -2,90 +2,195 @@ package de.paws.pixelwar;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
+import io.netty.handler.traffic.TrafficCounter;
+import java.util.HashMap;
import java.util.HashSet;
+import java.util.Map;
import java.util.Set;
+import org.apache.commons.lang3.StringUtils;
+
public class PixelClientHandler extends SimpleChannelInboundHandler {
+ public interface CommandHandler {
+ public void handle(PixelClientHandler client, String[] args);
+ }
+
private static Set clients = new HashSet<>();
private final NetCanvas canvas;
- private long connected;
- private long c = 0;
- private ChannelHandlerContext channel;
+ private ChannelHandlerContext channelContext;
+ private final Set subscriptions = new HashSet<>();
+ private final Map handlers = new HashMap<>();
- public PixelClientHandler(NetCanvas canvas) {
+ private long connectedTime;
+ private long pixelCount = 0;
+ private long missedMessages = 0;
+
+ public PixelClientHandler(final NetCanvas canvas) {
this.canvas = canvas;
+ final TrafficCounter tc = new TrafficCounter(null, null, null,
+ connectedTime);
+ }
+
+ public void installHandler(final String command,
+ final CommandHandler handler) {
+ handlers.put(command.toUpperCase(), handler);
}
@Override
- public void channelActive(ChannelHandlerContext ctx) throws Exception {
+ public void channelActive(final ChannelHandlerContext ctx) throws Exception {
super.channelActive(ctx);
- this.connected = System.currentTimeMillis();
- channel = ctx;
+ connectedTime = System.currentTimeMillis();
+ channelContext = ctx;
synchronized (clients) {
clients.add(this);
}
}
@Override
- public void channelInactive(ChannelHandlerContext ctx) throws Exception {
+ public void channelInactive(final ChannelHandlerContext ctx)
+ throws Exception {
super.channelActive(ctx);
- long passed = System.currentTimeMillis() - connected;
- System.out.print(c * 1000 / passed);
+ final long passed = System.currentTimeMillis() - connectedTime;
synchronized (clients) {
clients.remove(this);
}
}
- public void writeIfPossible(String str) {
- if (channel.channel().isWritable()) {
- channel.writeAndFlush(str.trim() + "\n");
+ public void writeIfPossible(final String str) {
+ if (channelContext.channel().isWritable()) {
+ channelContext.write(str + "\n");
+ } else {
+ missedMessages++;
}
}
+ private void writeChannel(final String channel, final String message) {
+ if (subscriptions.contains(channel) || subscriptions.contains("*")) {
+ writeIfPossible(message);
+ }
+ }
+
+ public void write(final String str) {
+ channelContext.writeAndFlush(str + "\n");
+ }
+
+ public void error(final String msg) {
+ write("ERR " + msg + "\n");
+ channelContext.close();
+ }
+
@Override
- protected void channelRead0(ChannelHandlerContext ctx, String msg)
- throws Exception {
- String[] parts = msg.split(" ");
- if (parts.length < 1)
+ protected void channelRead0(final ChannelHandlerContext ctx,
+ final String msg) throws Exception {
+ final int split = msg.indexOf(" ");
+ String command;
+ String data;
+ if (split == -1) {
+ command = msg.toUpperCase();
+ data = "";
+ } else {
+ command = msg.substring(0, split).toUpperCase();
+ data = msg.substring(split + 1).trim();
+ }
+
+ switch (command) {
+ case ("PX"):
+ handle_PX(ctx, data);
+ break;
+ case ("SIZE"):
+ handle_SIZE(ctx, data);
+ break;
+ case ("PUB"):
+ handle_PUB(ctx, data);
+ break;
+ case ("SUB"):
+ handle_SUB(ctx, data);
+ break;
+ case ("HELP"):
+ handle_HELP(ctx, data);
+ break;
+ default:
+ error("Unknown command");
+ }
+ }
+
+ private void handle_HELP(final ChannelHandlerContext ctx, final String data) {
+ writeIfPossible("HELP Commands:\n" + "HELP SIZE\n"
+ + "HELP PX \n" + "HELP PX \n"
+ + "HELP SUB\n" + "HELP SUB [-]\n"
+ + "HELP PUB \n");
+ }
+
+ private void handle_SUB(final ChannelHandlerContext ctx, final String data) {
+ if (data.startsWith("-")) {
+ final String channel = data.substring(1);
+ if (!subscriptions.remove(channel.toLowerCase())) {
+ error("Not subscribed to this channel.");
+ }
+ } else if (data.length() > 0) {
+ final String channel = data.toLowerCase();
+ if (!channel.matches("^[a-zA-Z0-9_]+$")) {
+ error("Invalid channel name");
+ } else if (subscriptions.size() >= 16) {
+ error("Too many subscriptions");
+ } else {
+ subscriptions.add(channel);
+ }
+ }
+
+ write("SUB " + StringUtils.join(subscriptions, " "));
+ }
+
+ private void handle_PUB(final ChannelHandlerContext ctx, final String data) {
+ final String[] parts = data.split(" ", 1);
+ if (parts.length != 2) {
+ error("Usage: PUB ");
return;
+ }
- String command = parts[0].toUpperCase();
+ final String channel = parts[0].toLowerCase();
+ final String message = "PUB " + channel + " " + parts[1].trim();
- if (command.equals("PX")) {
- c++;
- if (parts.length == 3) {
- int x = Integer.parseInt(parts[1]);
- int y = Integer.parseInt(parts[2]);
- int c = canvas.getPixel(x, y);
- ctx.write(String.format("PX %d %d %06x\n", x, y, c));
- ctx.flush();
- } else if (parts.length == 4) {
- int x = Integer.parseInt(parts[1]);
- int y = Integer.parseInt(parts[2]);
- int color = (int) Long.parseLong(parts[3], 16);
- if (parts[3].length() == 6)
+ for (final PixelClientHandler c : clients) {
+ if (c != this) {
+ c.writeChannel(channel, message);
+ }
+ }
+ }
+
+ private void handle_SIZE(final ChannelHandlerContext ctx, final String data) {
+ if (data.length() > 0) {
+ error("SIZE");
+ }
+ write(String
+ .format("SIZE %d %d", canvas.getWidth(), canvas.getHeight()));
+ }
+
+ private void handle_PX(final ChannelHandlerContext ctx, final String data) {
+ final String[] args = data.split(" ");
+ try {
+ pixelCount++;
+ if (args.length == 2) {
+ final int x = Integer.parseInt(args[0]);
+ final int y = Integer.parseInt(args[1]);
+ final int c = canvas.getPixel(x, y);
+ write(String.format("PX %d %d %06x", x, y, c));
+ } else if (args.length == 3) {
+ final int x = Integer.parseInt(args[0]);
+ final int y = Integer.parseInt(args[1]);
+ int color = (int) Long.parseLong(args[2], 16);
+ if (args[2].length() == 6) {
color += 0xff000000;
+ }
canvas.setPixel(x, y, color);
} else {
- ctx.writeAndFlush("ERR\n");
- ctx.close();
+ error("Usage: PX x y [rrggbb[aa]]");
}
- } else if (command.equals("SIZE")) {
- ctx.writeAndFlush(String.format("SIZE %d %d\n", canvas.getWidth(),
- canvas.getHeight()));
- } else if (command.equals("MSG")) {
- String text = command + " "
- + msg.substring(command.length()).trim();
- for (PixelClientHandler c : clients) {
- if (c != this)
- c.writeIfPossible(text);
- }
- } else {
- ctx.writeAndFlush("ERR\n");
- ctx.close();
+ } catch (final NumberFormatException e) {
+ error("Usage: PX x y [rrggbb[aa]]");
}
}
}
diff --git a/pixelwar/src/de/paws/pixelwar/PixelServer.java b/pixelwar/src/de/paws/pixelwar/PixelServer.java
index 9fcaa9a..2e351e1 100644
--- a/pixelwar/src/de/paws/pixelwar/PixelServer.java
+++ b/pixelwar/src/de/paws/pixelwar/PixelServer.java
@@ -22,25 +22,25 @@ public class PixelServer extends ChannelHandlerAdapter {
private final NetCanvas canvas;
private final int port;
- public PixelServer(int port) {
+ public PixelServer(final int port) {
this.port = port;
- this.canvas = new NetCanvas();
+ canvas = new NetCanvas();
}
public void run() throws InterruptedException {
- EventLoopGroup bossGroup = new NioEventLoopGroup(1);
- EventLoopGroup workerGroup = new NioEventLoopGroup();
+ final EventLoopGroup bossGroup = new NioEventLoopGroup(1);
+ final EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
- ServerBootstrap b = new ServerBootstrap();
+ final ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 100)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new ChannelInitializer() {
@Override
- public void initChannel(SocketChannel ch)
+ public void initChannel(final SocketChannel ch)
throws Exception {
- ChannelPipeline p = ch.pipeline();
+ final ChannelPipeline p = ch.pipeline();
// p.addLast("logger", new LoggingHandler(
// LogLevel.DEBUG));
@@ -55,7 +55,7 @@ public class PixelServer extends ChannelHandlerAdapter {
});
// Start the server.
- ChannelFuture f = b.bind(port).sync();
+ final ChannelFuture f = b.bind(port).sync();
// Wait until the server socket is closed.
f.channel().closeFuture().sync();
@@ -66,7 +66,7 @@ public class PixelServer extends ChannelHandlerAdapter {
}
}
- public static void main(String[] args) throws InterruptedException {
+ public static void main(final String[] args) throws InterruptedException {
new PixelServer(8080).run();
}