save/load buffers and client-pointer and more optimisation
This commit is contained in:
parent
fb9436c47e
commit
7f195ccdc4
5 changed files with 197 additions and 62 deletions
25
pixelwar/src/de/paws/pixelwar/Drawable.java
Normal file
25
pixelwar/src/de/paws/pixelwar/Drawable.java
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
package de.paws.pixelwar;
|
||||
|
||||
import java.awt.Graphics2D;
|
||||
|
||||
public abstract class Drawable {
|
||||
|
||||
private boolean alive = true;
|
||||
|
||||
public final boolean isAlive() {
|
||||
return alive;
|
||||
}
|
||||
|
||||
public final void setAlive(final boolean state) {
|
||||
alive = state;
|
||||
}
|
||||
|
||||
public void tick(final long dt) {
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
}
|
||||
|
||||
public abstract void draw(final Graphics2D g);
|
||||
|
||||
}
|
||||
46
pixelwar/src/de/paws/pixelwar/Label.java
Normal file
46
pixelwar/src/de/paws/pixelwar/Label.java
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
package de.paws.pixelwar;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Graphics2D;
|
||||
|
||||
public class Label extends Drawable {
|
||||
|
||||
public static boolean show = true;
|
||||
|
||||
float x = 0;
|
||||
float y = 0;
|
||||
int x2 = 0;
|
||||
int y2 = 0;
|
||||
String text = "";
|
||||
|
||||
public String getText() {
|
||||
return text;
|
||||
}
|
||||
|
||||
public void setText(final String text) {
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
public void setPos(final int x, final int y) {
|
||||
x2 = x;
|
||||
y2 = y;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick(final long dt) {
|
||||
x += ((x2 - x) * dt * 0.0001);
|
||||
y += ((y2 - y) * dt * 0.0001);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(final Graphics2D g) {
|
||||
if (!show) {
|
||||
return;
|
||||
}
|
||||
g.setColor(Color.WHITE);
|
||||
g.drawString(text, (int) x, (int) y);
|
||||
g.drawLine((int) x, (int) y, x2, y2);
|
||||
g.drawOval(x2 - 5, y2 - 5, 10, 10);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -11,20 +11,28 @@ import java.awt.event.ComponentEvent;
|
|||
import java.awt.event.ComponentListener;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.event.KeyListener;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.event.MouseMotionListener;
|
||||
import java.awt.image.BufferStrategy;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.swing.JFrame;
|
||||
|
||||
public class NetCanvas implements ComponentListener, KeyListener {
|
||||
private volatile BufferedImage drawBuffer;
|
||||
private volatile BufferedImage overlayBuffer;
|
||||
private volatile BufferedImage paintBuffer;
|
||||
|
||||
private BufferStrategy strategy;
|
||||
public class NetCanvas implements ComponentListener, KeyListener,
|
||||
MouseMotionListener {
|
||||
private volatile BufferedImage pxBuffer;
|
||||
private volatile BufferStrategy strategy;
|
||||
private final Thread refresher;
|
||||
private final JFrame frame;
|
||||
private final Canvas canvas;
|
||||
private final List<Drawable> drawables = new ArrayList<>();
|
||||
private long lastDraw;
|
||||
|
||||
public NetCanvas() {
|
||||
frame = new JFrame();
|
||||
|
|
@ -33,6 +41,7 @@ public class NetCanvas implements ComponentListener, KeyListener {
|
|||
|
||||
frame.addComponentListener(this);
|
||||
canvas.addKeyListener(this);
|
||||
canvas.addMouseMotionListener(this);
|
||||
|
||||
canvas.setSize(800, 600);
|
||||
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
|
|
@ -44,20 +53,36 @@ public class NetCanvas implements ComponentListener, KeyListener {
|
|||
frame.pack();
|
||||
frame.setVisible(true);
|
||||
|
||||
canvas.createBufferStrategy(2);
|
||||
strategy = canvas.getBufferStrategy();
|
||||
|
||||
refresher = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
lastDraw = System.currentTimeMillis() - 1;
|
||||
try {
|
||||
while (true) {
|
||||
draw();
|
||||
Thread.sleep(1000 / 30);
|
||||
final long d1 = System.currentTimeMillis();
|
||||
draw(d1 - lastDraw);
|
||||
final long d2 = System.currentTimeMillis();
|
||||
lastDraw = d1;
|
||||
Thread.sleep(Math.max(1, (1000 / 30) - (d2 - d1)));
|
||||
}
|
||||
} catch (final InterruptedException e) {
|
||||
}
|
||||
}
|
||||
});
|
||||
refresher.start();
|
||||
}
|
||||
|
||||
public void saveAs(final File file) throws IOException {
|
||||
ImageIO.write(pxBuffer, "png", file);
|
||||
}
|
||||
|
||||
public void loadFrom(final File file) throws IOException {
|
||||
final BufferedImage img = ImageIO.read(file);
|
||||
resizeBuffer(img.getWidth(), img.getHeight());
|
||||
blit(img, pxBuffer);
|
||||
}
|
||||
|
||||
private void blit(final BufferedImage source, final BufferedImage target) {
|
||||
|
|
@ -70,9 +95,9 @@ public class NetCanvas implements ComponentListener, KeyListener {
|
|||
BufferedImage newBuffer;
|
||||
int mw = w, mh = h;
|
||||
|
||||
if (drawBuffer != null) {
|
||||
mw = Math.max(w, drawBuffer.getWidth());
|
||||
mh = Math.max(h, drawBuffer.getHeight());
|
||||
if (pxBuffer != null) {
|
||||
mw = Math.max(w, pxBuffer.getWidth());
|
||||
mh = Math.max(h, pxBuffer.getHeight());
|
||||
if (mw > w && mh > h) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -80,51 +105,50 @@ public class NetCanvas implements ComponentListener, KeyListener {
|
|||
|
||||
newBuffer = frame.getGraphicsConfiguration().createCompatibleImage(w,
|
||||
h, Transparency.OPAQUE);
|
||||
if (drawBuffer != null) {
|
||||
blit(drawBuffer, newBuffer);
|
||||
if (pxBuffer != null) {
|
||||
blit(pxBuffer, newBuffer);
|
||||
}
|
||||
drawBuffer = newBuffer;
|
||||
pxBuffer = newBuffer;
|
||||
|
||||
overlayBuffer = frame.getGraphicsConfiguration().createCompatibleImage(
|
||||
w, h, Transparency.TRANSLUCENT);
|
||||
|
||||
paintBuffer = frame.getGraphicsConfiguration().createCompatibleImage(w,
|
||||
h, Transparency.OPAQUE);
|
||||
frame.getGraphicsConfiguration().createCompatibleImage(w, h,
|
||||
Transparency.OPAQUE);
|
||||
}
|
||||
|
||||
private void drawOverlay() {
|
||||
final Graphics2D g = overlayBuffer.createGraphics();
|
||||
g.setComposite(AlphaComposite.Clear);
|
||||
g.fillRect(0, 0, overlayBuffer.getWidth(), overlayBuffer.getHeight());
|
||||
private void drawOverlay(final Graphics2D g, final long dt) {
|
||||
g.setComposite(AlphaComposite.SrcOver);
|
||||
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
|
||||
RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
g.drawString(String.format("Pixelflut"), 2, 10);
|
||||
g.dispose();
|
||||
g.drawString(String.format("Pixelflut FPS:%.2f", 1000.0 / dt), 2, 10);
|
||||
|
||||
synchronized (drawables) {
|
||||
final Iterator<Drawable> i = drawables.iterator();
|
||||
while (i.hasNext()) {
|
||||
final Drawable d = i.next();
|
||||
if (d.isAlive()) {
|
||||
d.tick(dt);
|
||||
d.draw(g);
|
||||
} else {
|
||||
d.dispose();
|
||||
i.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
synchronized public void draw() {
|
||||
synchronized public void draw(final long dt) {
|
||||
if (!frame.isVisible()) {
|
||||
return;
|
||||
}
|
||||
drawOverlay();
|
||||
// Prepare composite buffer
|
||||
final Graphics2D g = paintBuffer.createGraphics();
|
||||
g.drawImage(drawBuffer, 0, 0, null);
|
||||
g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER));
|
||||
g.drawImage(overlayBuffer, 0, 0, null);
|
||||
final Graphics2D g = (Graphics2D) strategy.getDrawGraphics();
|
||||
g.drawImage(pxBuffer, 0, 0, null);
|
||||
drawOverlay(g, dt);
|
||||
g.dispose();
|
||||
|
||||
// Blit into double buffer
|
||||
final Graphics g2 = strategy.getDrawGraphics();
|
||||
g2.drawImage(paintBuffer, 0, 0, null);
|
||||
g2.dispose();
|
||||
strategy.show();
|
||||
}
|
||||
|
||||
public void setPixel(final int x, final int y, final int argb) {
|
||||
// Below this values 8bit color channels are nulled.
|
||||
final BufferedImage img = drawBuffer;
|
||||
final BufferedImage img = pxBuffer;
|
||||
|
||||
if (x >= 0 && x < img.getWidth() && y >= 0 && y < img.getHeight()) {
|
||||
final int alpha = (argb >>> 24) % 256;
|
||||
|
|
@ -157,7 +181,7 @@ public class NetCanvas implements ComponentListener, KeyListener {
|
|||
}
|
||||
|
||||
public int getPixel(final int x, final int y) {
|
||||
final BufferedImage img = drawBuffer;
|
||||
final BufferedImage img = pxBuffer;
|
||||
if (x >= 0 && x < img.getWidth() && y >= 0 && y < img.getHeight()) {
|
||||
return img.getRGB(x, y) & 0x00ffffff;
|
||||
}
|
||||
|
|
@ -165,23 +189,31 @@ public class NetCanvas implements ComponentListener, KeyListener {
|
|||
}
|
||||
|
||||
public int getWidth() {
|
||||
return drawBuffer.getWidth();
|
||||
return pxBuffer.getWidth();
|
||||
}
|
||||
|
||||
public int getHeight() {
|
||||
return drawBuffer.getHeight();
|
||||
return pxBuffer.getHeight();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyReleased(final KeyEvent e) {
|
||||
if (e.getKeyChar() == 'c') {
|
||||
final Graphics g = drawBuffer.getGraphics();
|
||||
final Graphics g = pxBuffer.getGraphics();
|
||||
g.setColor(Color.BLACK);
|
||||
g.fillRect(0, 0, drawBuffer.getWidth(), drawBuffer.getHeight());
|
||||
g.fillRect(0, 0, pxBuffer.getWidth(), pxBuffer.getHeight());
|
||||
g.dispose();
|
||||
} else if (e.getKeyChar() == 'q'
|
||||
|| e.getKeyCode() == KeyEvent.VK_ESCAPE) {
|
||||
System.exit(0);
|
||||
} else if (e.getKeyChar() == 'l') {
|
||||
Label.show = !Label.show;
|
||||
} else if (e.getKeyChar() == 's') {
|
||||
try {
|
||||
saveAs(new File("/tmp/canvas.png"));
|
||||
} catch (final IOException e1) {
|
||||
e1.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -212,4 +244,26 @@ public class NetCanvas implements ComponentListener, KeyListener {
|
|||
public void componentHidden(final ComponentEvent e) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseDragged(final MouseEvent e) {
|
||||
// System.err.println(e.getPoint());
|
||||
// setPixel(e.getX(), e.getY(), 0xffffffff);
|
||||
final Graphics2D g = (Graphics2D) pxBuffer.getGraphics();
|
||||
g.setColor(Color.MAGENTA);
|
||||
g.fillOval(e.getX() - 50, e.getY() - 50, 100, 100);
|
||||
g.dispose();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseMoved(final MouseEvent e) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
public void addDrawable(final Label label) {
|
||||
synchronized (drawables) {
|
||||
drawables.add(label);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ 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;
|
||||
|
|
@ -24,14 +23,10 @@ public class PixelClientHandler extends SimpleChannelInboundHandler<String> {
|
|||
private final Set<String> subscriptions = new HashSet<>();
|
||||
private final Map<String, CommandHandler> handlers = new HashMap<>();
|
||||
|
||||
private long connectedTime;
|
||||
private long pixelCount = 0;
|
||||
private long missedMessages = 0;
|
||||
private Label label;
|
||||
|
||||
public PixelClientHandler(final NetCanvas canvas) {
|
||||
this.canvas = canvas;
|
||||
final TrafficCounter tc = new TrafficCounter(null, null, null,
|
||||
connectedTime);
|
||||
}
|
||||
|
||||
public void installHandler(final String command,
|
||||
|
|
@ -42,7 +37,9 @@ public class PixelClientHandler extends SimpleChannelInboundHandler<String> {
|
|||
@Override
|
||||
public void channelActive(final ChannelHandlerContext ctx) throws Exception {
|
||||
super.channelActive(ctx);
|
||||
connectedTime = System.currentTimeMillis();
|
||||
label = new Label();
|
||||
label.setText(ctx.channel().remoteAddress().toString());
|
||||
canvas.addDrawable(label);
|
||||
channelContext = ctx;
|
||||
synchronized (clients) {
|
||||
clients.add(this);
|
||||
|
|
@ -53,17 +50,17 @@ public class PixelClientHandler extends SimpleChannelInboundHandler<String> {
|
|||
public void channelInactive(final ChannelHandlerContext ctx)
|
||||
throws Exception {
|
||||
super.channelActive(ctx);
|
||||
final long passed = System.currentTimeMillis() - connectedTime;
|
||||
synchronized (clients) {
|
||||
clients.remove(this);
|
||||
}
|
||||
label.setAlive(false);
|
||||
}
|
||||
|
||||
public void writeIfPossible(final String str) {
|
||||
if (channelContext.channel().isWritable()) {
|
||||
channelContext.write(str + "\n");
|
||||
channelContext.flush();
|
||||
} else {
|
||||
missedMessages++;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -145,7 +142,7 @@ public class PixelClientHandler extends SimpleChannelInboundHandler<String> {
|
|||
}
|
||||
|
||||
private void handle_PUB(final ChannelHandlerContext ctx, final String data) {
|
||||
final String[] parts = data.split(" ", 1);
|
||||
final String[] parts = data.split(" ", 2);
|
||||
if (parts.length != 2) {
|
||||
error("Usage: PUB <channel> <message>");
|
||||
return;
|
||||
|
|
@ -155,9 +152,7 @@ public class PixelClientHandler extends SimpleChannelInboundHandler<String> {
|
|||
final String message = "PUB " + channel + " " + parts[1].trim();
|
||||
|
||||
for (final PixelClientHandler c : clients) {
|
||||
if (c != this) {
|
||||
c.writeChannel(channel, message);
|
||||
}
|
||||
c.writeChannel(channel, message);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -172,7 +167,6 @@ public class PixelClientHandler extends SimpleChannelInboundHandler<String> {
|
|||
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]);
|
||||
|
|
@ -185,6 +179,7 @@ public class PixelClientHandler extends SimpleChannelInboundHandler<String> {
|
|||
if (args[2].length() == 6) {
|
||||
color += 0xff000000;
|
||||
}
|
||||
label.setPos(x, y);
|
||||
canvas.setPixel(x, y, color);
|
||||
} else {
|
||||
error("Usage: PX x y [rrggbb[aa]]");
|
||||
|
|
|
|||
|
|
@ -17,17 +17,26 @@ import io.netty.handler.codec.string.StringEncoder;
|
|||
import io.netty.handler.logging.LogLevel;
|
||||
import io.netty.handler.logging.LoggingHandler;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
public class PixelServer extends ChannelHandlerAdapter {
|
||||
|
||||
private final NetCanvas canvas;
|
||||
private final int port;
|
||||
private final File savefile = new File("/tmp/canvas.png");
|
||||
|
||||
public PixelServer(final int port) {
|
||||
public PixelServer(final int port) throws IOException {
|
||||
this.port = port;
|
||||
canvas = new NetCanvas();
|
||||
if (savefile.exists()) {
|
||||
canvas.loadFrom(savefile);
|
||||
}
|
||||
}
|
||||
|
||||
public void run() throws InterruptedException {
|
||||
public void run() throws InterruptedException, UnknownHostException {
|
||||
final EventLoopGroup bossGroup = new NioEventLoopGroup(1);
|
||||
final EventLoopGroup workerGroup = new NioEventLoopGroup();
|
||||
try {
|
||||
|
|
@ -48,14 +57,14 @@ public class PixelServer extends ChannelHandlerAdapter {
|
|||
128, Delimiters.lineDelimiter()));
|
||||
p.addLast("decoder", new StringDecoder());
|
||||
p.addLast("encoder", new StringEncoder());
|
||||
|
||||
// and then business logic.
|
||||
p.addLast("handler", new PixelClientHandler(canvas));
|
||||
}
|
||||
});
|
||||
|
||||
// Start the server.
|
||||
final ChannelFuture f = b.bind(port).sync();
|
||||
final ChannelFuture f = b.bind(
|
||||
new InetSocketAddress("0.0.0.0", port)).sync();
|
||||
|
||||
// Wait until the server socket is closed.
|
||||
f.channel().closeFuture().sync();
|
||||
|
|
@ -63,10 +72,16 @@ public class PixelServer extends ChannelHandlerAdapter {
|
|||
// Shut down all event loops to terminate all threads.
|
||||
bossGroup.shutdownGracefully();
|
||||
workerGroup.shutdownGracefully();
|
||||
try {
|
||||
canvas.saveAs(savefile);
|
||||
} catch (final IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(final String[] args) throws InterruptedException {
|
||||
public static void main(final String[] args) throws InterruptedException,
|
||||
IOException {
|
||||
new PixelServer(8080).run();
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue