Added java implementation
This commit is contained in:
parent
5911691434
commit
75b436a29a
8 changed files with 387 additions and 3 deletions
60
pixelwar/pom.xml
Normal file
60
pixelwar/pom.xml
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>pixelwar</groupId>
|
||||
<artifactId>pixelwar</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<build>
|
||||
<sourceDirectory>src</sourceDirectory>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.1</version>
|
||||
<configuration>
|
||||
<source>1.7</source>
|
||||
<target>1.7</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifest>
|
||||
<mainClass>de.paws.pixelwar.PixelServer</mainClass>
|
||||
</manifest>
|
||||
</archive>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifest>
|
||||
<mainClass>de.paws.pixelwar.PixelServer</mainClass>
|
||||
</manifest>
|
||||
</archive>
|
||||
<descriptorRefs>
|
||||
<descriptorRef>jar-with-dependencies</descriptorRef>
|
||||
</descriptorRefs>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>make-assembly</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>single</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-all</artifactId>
|
||||
<version>4.0.18.Final</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
182
pixelwar/src/de/paws/pixelwar/Canvas.java
Normal file
182
pixelwar/src/de/paws/pixelwar/Canvas.java
Normal file
|
|
@ -0,0 +1,182 @@
|
|||
package de.paws.pixelwar;
|
||||
|
||||
import java.awt.AlphaComposite;
|
||||
import java.awt.Color;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.RenderingHints;
|
||||
import java.awt.Transparency;
|
||||
import java.awt.event.ComponentEvent;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.image.BufferStrategy;
|
||||
import java.awt.image.BufferedImage;
|
||||
|
||||
import javax.swing.JFrame;
|
||||
|
||||
public class Canvas {
|
||||
private volatile BufferedImage drawBuffer;
|
||||
private volatile BufferedImage overlayBuffer;
|
||||
private volatile BufferedImage paintBuffer;
|
||||
|
||||
private final BufferStrategy strategy;
|
||||
private final Thread refresher;
|
||||
private final JFrame frame;
|
||||
|
||||
public Canvas() {
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void processComponentEvent(ComponentEvent e) {
|
||||
super.processComponentEvent(e);
|
||||
if (e.getID() == ComponentEvent.COMPONENT_RESIZED) {
|
||||
resizeBuffer(getWidth(), getHeight());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
frame.setUndecorated(true);
|
||||
frame.setResizable(true);
|
||||
frame.setSize(800, 600);
|
||||
frame.setVisible(true);
|
||||
frame.setAlwaysOnTop(true);
|
||||
|
||||
frame.createBufferStrategy(2);
|
||||
strategy = frame.getBufferStrategy();
|
||||
resizeBuffer(1024, 1024);
|
||||
|
||||
refresher = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
while (true) {
|
||||
draw();
|
||||
Thread.sleep(1000 / 30);
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
}
|
||||
});
|
||||
refresher.start();
|
||||
}
|
||||
|
||||
private void blit(BufferedImage source, BufferedImage target) {
|
||||
Graphics g = target.getGraphics();
|
||||
g.drawImage(source, 0, 0, null);
|
||||
g.dispose();
|
||||
}
|
||||
|
||||
synchronized private void resizeBuffer(int w, 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)
|
||||
return;
|
||||
}
|
||||
|
||||
newBuffer = frame.getGraphicsConfiguration().createCompatibleImage(w,
|
||||
h, Transparency.OPAQUE);
|
||||
if (drawBuffer != null)
|
||||
blit(drawBuffer, newBuffer);
|
||||
drawBuffer = newBuffer;
|
||||
|
||||
overlayBuffer = frame.getGraphicsConfiguration().createCompatibleImage(
|
||||
w, h, Transparency.TRANSLUCENT);
|
||||
|
||||
paintBuffer = frame.getGraphicsConfiguration().createCompatibleImage(w,
|
||||
h, Transparency.OPAQUE);
|
||||
}
|
||||
|
||||
private void drawOverlay() {
|
||||
Graphics2D g = overlayBuffer.createGraphics();
|
||||
g.setComposite(AlphaComposite.Clear);
|
||||
g.fillRect(0, 0, overlayBuffer.getWidth(), overlayBuffer.getHeight());
|
||||
g.setComposite(AlphaComposite.SrcOver);
|
||||
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
|
||||
RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
g.drawString(String.format("Pixelflut"), 2, 10);
|
||||
g.dispose();
|
||||
}
|
||||
|
||||
synchronized public void draw() {
|
||||
drawOverlay();
|
||||
// Prepare composite buffer
|
||||
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();
|
||||
g2.drawImage(paintBuffer, 0, 0, null);
|
||||
g2.dispose();
|
||||
strategy.show();
|
||||
}
|
||||
|
||||
public void setPixel(int x, int y, int argb) {
|
||||
// Below this values 8bit color channels are nulled.
|
||||
BufferedImage img = drawBuffer;
|
||||
|
||||
if (x >= 0 && x < img.getWidth() && y >= 0 && y < img.getHeight()) {
|
||||
int alpha = (argb >>> 24) % 256;
|
||||
int rgb = argb & 0xffffff;
|
||||
|
||||
if (alpha < 1)
|
||||
return;
|
||||
|
||||
if (alpha < 255) {
|
||||
int target = img.getRGB(x, y);
|
||||
int r, g, b;
|
||||
|
||||
r = ((target >>> 16) & 0xff) * (255 - alpha);
|
||||
g = ((target >>> 8) & 0xff) * (255 - alpha);
|
||||
b = ((target >>> 0) & 0xff) * (255 - alpha);
|
||||
|
||||
r += ((argb >>> 16) & 0xff) * alpha;
|
||||
g += ((argb >>> 8) & 0xff) * alpha;
|
||||
b += ((argb >>> 0) & 0xff) * alpha;
|
||||
|
||||
rgb = 0xff << 24;
|
||||
rgb += (r / 255) << 16;
|
||||
rgb += (g / 255) << 8;
|
||||
rgb += (b / 255) << 0;
|
||||
}
|
||||
|
||||
img.setRGB(x, y, rgb);
|
||||
}
|
||||
}
|
||||
|
||||
public int getPixel(int x, int y) {
|
||||
BufferedImage img = drawBuffer;
|
||||
if (x >= 0 && x < img.getWidth() && y >= 0 && y < img.getHeight())
|
||||
return img.getRGB(x, y) & 0x00ffffff;
|
||||
return 0;
|
||||
}
|
||||
|
||||
public int getWidth() {
|
||||
return drawBuffer.getWidth();
|
||||
}
|
||||
|
||||
public int getHeight() {
|
||||
return drawBuffer.getHeight();
|
||||
}
|
||||
|
||||
}
|
||||
46
pixelwar/src/de/paws/pixelwar/PixelClientHandler.java
Normal file
46
pixelwar/src/de/paws/pixelwar/PixelClientHandler.java
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
package de.paws.pixelwar;
|
||||
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
|
||||
public class PixelClientHandler extends SimpleChannelInboundHandler<String> {
|
||||
|
||||
private final Canvas canvas;
|
||||
|
||||
public PixelClientHandler(Canvas canvas) {
|
||||
this.canvas = canvas;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void channelRead0(ChannelHandlerContext ctx, String msg)
|
||||
throws Exception {
|
||||
String[] parts = msg.split(" ");
|
||||
if (parts.length < 1)
|
||||
return;
|
||||
|
||||
String command = parts[0].toUpperCase();
|
||||
|
||||
if (command.equals("PX")) {
|
||||
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)
|
||||
color += 0xff000000;
|
||||
canvas.setPixel(x, y, color);
|
||||
}
|
||||
} else if (command.equals("SIZE")) {
|
||||
ctx.write(String.format("SIZE %d %d\n", canvas.getWidth(),
|
||||
canvas.getHeight()));
|
||||
ctx.flush();
|
||||
} else if (command.equals("TILE") && parts.length == 3) {
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
74
pixelwar/src/de/paws/pixelwar/PixelServer.java
Normal file
74
pixelwar/src/de/paws/pixelwar/PixelServer.java
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
package de.paws.pixelwar;
|
||||
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelHandlerAdapter;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.ChannelOption;
|
||||
import io.netty.channel.EventLoopGroup;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
||||
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
|
||||
import io.netty.handler.codec.Delimiters;
|
||||
import io.netty.handler.codec.string.StringDecoder;
|
||||
import io.netty.handler.codec.string.StringEncoder;
|
||||
import io.netty.handler.logging.LogLevel;
|
||||
import io.netty.handler.logging.LoggingHandler;
|
||||
|
||||
public class PixelServer extends ChannelHandlerAdapter {
|
||||
|
||||
private final Canvas canvas;
|
||||
private final int port;
|
||||
|
||||
public PixelServer(int port) {
|
||||
this.port = port;
|
||||
this.canvas = new Canvas();
|
||||
}
|
||||
|
||||
public void run() throws InterruptedException {
|
||||
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
|
||||
EventLoopGroup workerGroup = new NioEventLoopGroup();
|
||||
try {
|
||||
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)
|
||||
throws Exception {
|
||||
|
||||
ch.pipeline().addLast(
|
||||
"framer",
|
||||
new DelimiterBasedFrameDecoder(128,
|
||||
Delimiters.lineDelimiter()));
|
||||
ch.pipeline().addLast("decoder",
|
||||
new StringDecoder());
|
||||
ch.pipeline().addLast("encoder",
|
||||
new StringEncoder());
|
||||
|
||||
// and then business logic.
|
||||
ch.pipeline().addLast("handler",
|
||||
new PixelClientHandler(canvas));
|
||||
}
|
||||
});
|
||||
|
||||
// Start the server.
|
||||
ChannelFuture f = b.bind(port).sync();
|
||||
|
||||
// Wait until the server socket is closed.
|
||||
f.channel().closeFuture().sync();
|
||||
} finally {
|
||||
// Shut down all event loops to terminate all threads.
|
||||
bossGroup.shutdownGracefully();
|
||||
workerGroup.shutdownGracefully();
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws InterruptedException {
|
||||
new PixelServer(8080).run();
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue