Added java implementation

This commit is contained in:
Marcel Hellkamp 2014-05-07 15:56:04 +02:00
parent 5911691434
commit 75b436a29a
8 changed files with 387 additions and 3 deletions

View file

@ -1,5 +1,27 @@
Install Pixelflut & Pixelwar
======= ====================
Multiplayer canvas.
Pixelflut (python)
------------------
Gevent and pygame based python implementation. easier to hack with, but a bit slow. Not recommended for more than 20 players.
sudo aptitude install python-gevent python-pygame sudo aptitude install python-gevent python-pygame
python pixelflut.py cd pixelflut
python pixelflut.py brain.py
Pixelwar (java)
---------------
Netty based java7 implementation. Very fast but not scriptable (yet).
sudo aptitude install maven2 openjdk-7-jdk
cd pixelwar
mvn package
java -jar package/pixelwar*-jar-with-dependencies.jar

View file

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

Before After
Before After

60
pixelwar/pom.xml Normal file
View 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>

View 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();
}
}

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

View 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();
}
}