PUB/SUB and HELP commands.

Several GUI fixes.
This commit is contained in:
Marcel Hellkamp 2015-02-10 15:01:35 +01:00
parent ad19b4ffb3
commit fb9436c47e
5 changed files with 255 additions and 118 deletions

View file

@ -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

View file

@ -56,5 +56,10 @@
<artifactId>netty-all</artifactId>
<version>4.0.18.Final</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.3.2</version>
</dependency>
</dependencies>
</project>

View file

@ -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) {
}
}

View file

@ -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<String> {
public interface CommandHandler {
public void handle(PixelClientHandler client, String[] args);
}
private static Set<PixelClientHandler> clients = new HashSet<>();
private final NetCanvas canvas;
private long connected;
private long c = 0;
private ChannelHandlerContext channel;
private ChannelHandlerContext channelContext;
private final Set<String> subscriptions = new HashSet<>();
private final Map<String, CommandHandler> 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 <x> <y>\n" + "HELP PX <x> <y> <rrggbb[aa]>\n"
+ "HELP SUB\n" + "HELP SUB [-]<channel>\n"
+ "HELP PUB <channel> <message>\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 <channel> <message>");
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]]");
}
}
}

View file

@ -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<SocketChannel>() {
@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();
}