Added java implementation
This commit is contained in:
parent
5911691434
commit
75b436a29a
8 changed files with 387 additions and 3 deletions
28
README.txt
28
README.txt
|
|
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.6 KiB |
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