MaimMim Man in the middle minetest interceptor
- prototype - between client and server, allow to capture all exchanges and potentially change them - created to capture server maps in laby - first test get only MapBlock, support version serialization version 28 - prepartion for 29 with zstd but untested.
This commit is contained in:
6
.idea/ant.xml
generated
Normal file
6
.idea/ant.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="AntConfiguration">
|
||||
<buildFile url="file://$PROJECT_DIR$/build.xml" />
|
||||
</component>
|
||||
</project>
|
||||
1
.idea/laby.iml
generated
1
.idea/laby.iml
generated
@@ -25,5 +25,6 @@
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" name="artgaphics-0.2.0" level="project" />
|
||||
<orderEntry type="library" name="zstd-jni-1.5.2-2" level="project" />
|
||||
</component>
|
||||
</module>
|
||||
9
.idea/libraries/artgaphics_0_1_0.xml
generated
9
.idea/libraries/artgaphics_0_1_0.xml
generated
@@ -1,9 +0,0 @@
|
||||
<component name="libraryTable">
|
||||
<library name="artgaphics-0.1.0">
|
||||
<CLASSES>
|
||||
<root url="jar://$PROJECT_DIR$/libs/artgaphics-0.1.0.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</component>
|
||||
9
.idea/libraries/zstd_jni_1_5_2_2.xml
generated
Normal file
9
.idea/libraries/zstd_jni_1_5_2_2.xml
generated
Normal file
@@ -0,0 +1,9 @@
|
||||
<component name="libraryTable">
|
||||
<library name="zstd-jni-1.5.2-2">
|
||||
<CLASSES>
|
||||
<root url="jar://$PROJECT_DIR$/libs/zstd-jni-1.5.2-2.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</component>
|
||||
@@ -10,8 +10,11 @@
|
||||
<property name="src" location="java"/>
|
||||
<property name="build" location="build"/>
|
||||
<property name="dist" location="dist"/>
|
||||
<!-- Fill me please / todo -->
|
||||
<!-- Fill me please / todo
|
||||
see fetch_dependencies
|
||||
-->
|
||||
<property name="artgraphicslib" value="artgaphics-0.2.0"/>
|
||||
<property name="zstd-jnilib" value="zstd-jni-1.5.2-2"/>
|
||||
<target name="gather_project_params">
|
||||
<!-- original source parameter is in project_params project_version -->
|
||||
<loadfile property="distversion" srcfile="${basedir}/project_params">
|
||||
@@ -36,7 +39,7 @@
|
||||
<target name="compile" depends="init"
|
||||
description="compile the source " >
|
||||
<!-- Compile the java code from ${src} into ${build} -->
|
||||
<javac srcdir="${src}" destdir="${build}" includeantruntime="false" classpath="libs/${artgraphicslib}.jar">
|
||||
<javac srcdir="${src}" destdir="${build}" includeantruntime="false" classpath="libs/${artgraphicslib}.jar:libs/${zstd-jnilib}.jar">
|
||||
<exclude name="org/artisanlogiciel/games/javafx/*"/>
|
||||
<compilerarg value="-Xlint:deprecation,unchecked" />
|
||||
</javac>
|
||||
|
||||
@@ -14,6 +14,18 @@ fetch_jackson_databind()
|
||||
fi
|
||||
}
|
||||
|
||||
fetch_zstd-jni()
|
||||
{
|
||||
|
||||
echo "compiled from https://github.com/luben/zstd-jni commit 54d3d50c16d96bd8a30e2d4c0a9648001a52d6f9"
|
||||
# had to hack through jar generation ...
|
||||
# cp ~/artisanlogiciel/ext_projects/java/zstd-jni/target/zstd-jni-1.5.2-2.jar .
|
||||
libversion_zstdjni=1.5.2-2
|
||||
pushd libs
|
||||
wget https://repo1.maven.org/maven2/com/github/luben/zstd-jni/${libversion_zstdjni}/zstd-jni-${libversion_zstdjni}.jar
|
||||
popd
|
||||
}
|
||||
|
||||
if [[ ! -d ../sharedrawweb ]]
|
||||
then
|
||||
echo "[ERROR] expected a sharedrawweb project parent ( for exports )" >&2
|
||||
@@ -35,3 +47,5 @@ if [[ -n $use_jackson ]]
|
||||
then
|
||||
fetch_jackson_databind
|
||||
fi
|
||||
|
||||
fetch_zstd-jni
|
||||
|
||||
26
java/org/artisanlogiciel/games/maze/MainMim.java
Normal file
26
java/org/artisanlogiciel/games/maze/MainMim.java
Normal file
@@ -0,0 +1,26 @@
|
||||
package org.artisanlogiciel.games.maze;
|
||||
|
||||
import org.artisanlogiciel.games.minetest.net.MiM;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
|
||||
public class MainMim {
|
||||
|
||||
void minetestMime(String serverName, int port) {
|
||||
MiM mim = new MiM(30002, new InetSocketAddress(serverName, port));
|
||||
mim.launch();
|
||||
}
|
||||
|
||||
public static void main(String pArgs[])
|
||||
{
|
||||
if ( pArgs.length > 1)
|
||||
{
|
||||
new MainMim().minetestMime(pArgs[0], Integer.parseInt(pArgs[1]));
|
||||
}
|
||||
else
|
||||
{
|
||||
System.err.println("[ERROR] please set minetest server hostname and port as arguments");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import org.artisanlogiciel.games.maze.*;
|
||||
import org.artisanlogiciel.games.maze.model.WidthHeightProvider;
|
||||
import org.artisanlogiciel.games.maze.persist.MazePersistRaw;
|
||||
import org.artisanlogiciel.games.maze.solve.SolvingModel;
|
||||
import org.artisanlogiciel.games.minetest.net.MiM;
|
||||
import org.artisanlogiciel.graphics.Drawing;
|
||||
import org.artisanlogiciel.xpm.Xpm;
|
||||
|
||||
@@ -14,6 +15,7 @@ import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
@@ -270,6 +272,7 @@ implements StatusListener
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
System.out.println("Default Locale " + Locale.getDefault());
|
||||
|
||||
if ((pArgs.length > 0) && (pArgs[0].length() > 0)) {
|
||||
|
||||
97
java/org/artisanlogiciel/games/minetest/MapBlock.java
Normal file
97
java/org/artisanlogiciel/games/minetest/MapBlock.java
Normal file
@@ -0,0 +1,97 @@
|
||||
package org.artisanlogiciel.games.minetest;
|
||||
|
||||
import com.github.luben.zstd.Zstd;
|
||||
import org.artisanlogiciel.games.minetest.core.Constant;
|
||||
import org.artisanlogiciel.games.minetest.core.PacketException;
|
||||
import org.artisanlogiciel.games.minetest.core.Serialize;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* A block of nodes of MAP_BLOCKSIZE in any x,y,z direction
|
||||
*/
|
||||
public class MapBlock {
|
||||
|
||||
final static int nodecount = Constant.MAP_BLOCKSIZE * Constant.MAP_BLOCKSIZE * Constant.MAP_BLOCKSIZE;
|
||||
|
||||
int m_lighting_complete;
|
||||
|
||||
ArrayList<MapNode> nodes;
|
||||
|
||||
ArrayList<MapNode> getNodes()
|
||||
{
|
||||
if ( nodes == null )
|
||||
{
|
||||
nodes = new ArrayList<>(nodecount);
|
||||
}
|
||||
return nodes;
|
||||
}
|
||||
|
||||
// in memory case
|
||||
public void deSerialize(ByteBuffer buffer, int version)
|
||||
throws PacketException
|
||||
{
|
||||
|
||||
int flags = 0;
|
||||
int content_width = 0;
|
||||
int params_width = 0;
|
||||
|
||||
if ( version >= 29) {
|
||||
// ByteBuffer byteBuffer = ByteBuffer.wrap(networkPacket.getBuffer(),offset,networkPacket.getLength() - offset);
|
||||
|
||||
/*
|
||||
// force to have a direct buffer.
|
||||
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(networkPacket.getLength());
|
||||
byteBuffer.put(networkPacket.getBuffer());
|
||||
byteBuffer.position(networkPacket.getOffset());
|
||||
|
||||
//System.arraycopy(networkPacket.getBuffer(), 0, byteBuffer.array(), 0, length);
|
||||
// zstd compression
|
||||
// FIXME, can we know original size ? somehow related to BLOCKSIZE³
|
||||
long originalSizeMax = Zstd.decompressedSize(byteBuffer);
|
||||
System.out.println(String.format("originalSizeMax=%d", originalSizeMax));
|
||||
ByteBuffer decompressed = Zstd.decompress(byteBuffer,(int) originalSizeMax);
|
||||
*/
|
||||
byte out[] = new byte[16386];
|
||||
long outLength = Zstd.decompressByteArray(out, 0, out.length, buffer.array(), buffer.arrayOffset(), buffer.limit());
|
||||
|
||||
// Should be a MapBlock
|
||||
MapBlock block = new MapBlock();
|
||||
ByteBuffer decompressed = ByteBuffer.wrap(out, 0, (int) outLength);
|
||||
buffer = decompressed;
|
||||
}
|
||||
|
||||
flags = Serialize.readU8(buffer);
|
||||
|
||||
if ( version >= 27 ) {
|
||||
m_lighting_complete = Serialize.readU16(buffer);
|
||||
}
|
||||
|
||||
content_width = Serialize.readU8(buffer);
|
||||
params_width = Serialize.readU8(buffer);
|
||||
|
||||
System.out.println(String.format(" flags %x lightning_complete %d content_witd %d params_width %d ",flags, m_lighting_complete, content_width, params_width));
|
||||
|
||||
if (( version < 29 ) && ( version >= 11 ))
|
||||
{
|
||||
// ZLib decompression
|
||||
try {
|
||||
byte decompressed[] = Serialize.decompress(buffer);
|
||||
buffer = ByteBuffer.wrap(decompressed);
|
||||
}
|
||||
catch ( Exception e)
|
||||
{
|
||||
// TODO
|
||||
System.err.println(e);
|
||||
e.printStackTrace(System.err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
nodes = getNodes();
|
||||
MapNode.deSerializeBulk(buffer,version, nodes,nodecount,content_width,params_width);
|
||||
|
||||
//m_node_metadata.deSerialize(is, m_gamedef->idef());
|
||||
}
|
||||
}
|
||||
99
java/org/artisanlogiciel/games/minetest/MapNode.java
Normal file
99
java/org/artisanlogiciel/games/minetest/MapNode.java
Normal file
@@ -0,0 +1,99 @@
|
||||
package org.artisanlogiciel.games.minetest;
|
||||
|
||||
import org.artisanlogiciel.games.minetest.core.PacketException;
|
||||
import org.artisanlogiciel.games.minetest.core.Serialize;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.List;
|
||||
|
||||
public class MapNode {
|
||||
/*
|
||||
Main content
|
||||
*/
|
||||
short param0;
|
||||
|
||||
/*
|
||||
Misc parameter. Initialized to 0.
|
||||
- For light_propagates() blocks, this is light intensity,
|
||||
stored logarithmically from 0 to LIGHT_MAX.
|
||||
Sunlight is LIGHT_SUN, which is LIGHT_MAX+1.
|
||||
- Contains 2 values, day- and night lighting. Each takes 4 bits.
|
||||
- Uhh... well, most blocks have light or nothing in here.
|
||||
*/
|
||||
byte param1;
|
||||
|
||||
/*
|
||||
The second parameter. Initialized to 0.
|
||||
E.g. direction for torches and flowing water.
|
||||
*/
|
||||
byte param2;
|
||||
|
||||
public void deSerialize(ByteBuffer buffer)
|
||||
throws PacketException
|
||||
{
|
||||
// version >= 24
|
||||
param0 = (short) Serialize.readU16(buffer);
|
||||
buffer.position(buffer.position() + 2);
|
||||
param1 = (byte) Serialize.readU8(buffer);
|
||||
buffer.position(buffer.position() + 1);
|
||||
param2 = (byte) Serialize.readU8(buffer);
|
||||
// network specific ...
|
||||
// readU8(is);
|
||||
|
||||
}
|
||||
|
||||
// Deserialize bulk node data
|
||||
public static void deSerializeBulk(ByteBuffer buffer, int version,
|
||||
List<MapNode> nodes, int nodecount,
|
||||
int content_width, int params_width)
|
||||
throws PacketException
|
||||
{
|
||||
byte[] intern = buffer.array();
|
||||
int initialOffset = buffer.position();
|
||||
|
||||
// Deserialize content
|
||||
if(content_width == 1)
|
||||
{
|
||||
for(int i=0; i<nodecount; i++) {
|
||||
nodes.add(i, new MapNode());
|
||||
nodes.get(i).param0 = (short) Serialize.readU8( intern, initialOffset + i, intern.length);
|
||||
}
|
||||
}
|
||||
else if(content_width == 2)
|
||||
{
|
||||
for(int i=0; i<nodecount; i++) {
|
||||
nodes.add(i, new MapNode());
|
||||
nodes.get(i).param0 = (short) Serialize.readU8( intern, initialOffset + (i*2), intern.length);
|
||||
}
|
||||
}
|
||||
|
||||
// Deserialize param1
|
||||
int start1 = initialOffset + (content_width * nodecount);
|
||||
for(int i=0; i<nodecount; i++) {
|
||||
nodes.get(i).param1 = (byte) Serialize.readU8( intern, start1 + i, intern.length);
|
||||
}
|
||||
|
||||
// Deserialize param2
|
||||
int start2 = initialOffset + ((content_width + 1) * nodecount);
|
||||
if(content_width == 1)
|
||||
{
|
||||
for(int i=0; i<nodecount; i++) {
|
||||
MapNode node = nodes.get(i);
|
||||
node.param2 = (byte) Serialize.readU8( intern, start2 + i, intern.length);
|
||||
if(node.param0 > 0x7F){
|
||||
node.param0 <<= 4;
|
||||
node.param0 |= (node.param2&0xF0)>>4;
|
||||
node.param2 &= 0x0F;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(content_width == 2)
|
||||
{
|
||||
for(int i=0; i<nodecount; i++) {
|
||||
nodes.get(i).param2 = (byte) Serialize.readU8(intern, start2 + i, intern.length);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package org.artisanlogiciel.games.minetest;
|
||||
import java.util.HashMap;
|
||||
|
||||
public class Node {
|
||||
// could be a core.v3s16
|
||||
int x;
|
||||
int y;
|
||||
int z;
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
package org.artisanlogiciel.games.minetest.core;
|
||||
|
||||
public class Constant {
|
||||
|
||||
public final static int MAP_BLOCKSIZE = 16;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package org.artisanlogiciel.games.minetest.core;
|
||||
|
||||
public class PacketException
|
||||
extends Exception
|
||||
{
|
||||
public PacketException(String message)
|
||||
{
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
116
java/org/artisanlogiciel/games/minetest/core/Serialize.java
Normal file
116
java/org/artisanlogiciel/games/minetest/core/Serialize.java
Normal file
@@ -0,0 +1,116 @@
|
||||
package org.artisanlogiciel.games.minetest.core;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.zip.Inflater;
|
||||
|
||||
public class Serialize {
|
||||
|
||||
public final static int BUFFER_SIZE = 16386;
|
||||
|
||||
public static int readS16(byte buffer[], int offset, int length)
|
||||
throws PacketException
|
||||
{
|
||||
if ( offset + 2 < length) {
|
||||
return 256 * buffer[offset] + buffer[offset + 1];
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new PacketException("out of bound offset U16");
|
||||
}
|
||||
}
|
||||
|
||||
// MSB network order
|
||||
public static int readU32(byte[] buffer, int offset, int length)
|
||||
throws PacketException
|
||||
{
|
||||
if ( offset + 4 < length) {
|
||||
return 16777216 * Byte.toUnsignedInt(buffer[offset]) +
|
||||
65536 * Byte.toUnsignedInt(buffer[offset + 1]) +
|
||||
256 * Byte.toUnsignedInt(buffer[offset + 2]) +
|
||||
Byte.toUnsignedInt(buffer[offset + 3]);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new PacketException("out of bound offset U32");
|
||||
}
|
||||
}
|
||||
|
||||
// MSB network order
|
||||
public static int readU16(byte[] buffer, int offset, int length)
|
||||
throws PacketException
|
||||
{
|
||||
if ( offset + 2 < length) {
|
||||
return 256 * Byte.toUnsignedInt(buffer[offset]) +
|
||||
Byte.toUnsignedInt(buffer[offset + 1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new PacketException("out of bound offset U16");
|
||||
}
|
||||
}
|
||||
|
||||
// will update position
|
||||
public static int readU16(ByteBuffer byteBuffer)
|
||||
throws PacketException
|
||||
{
|
||||
int u16 = readU16(byteBuffer.array(), byteBuffer.position(), byteBuffer.array().length);
|
||||
byteBuffer.position(byteBuffer.position()+2);
|
||||
return u16;
|
||||
}
|
||||
|
||||
public static int readU8(byte[] buffer, int offset, int length)
|
||||
throws PacketException
|
||||
{
|
||||
if ( offset + 1 < length) {
|
||||
return Byte.toUnsignedInt(buffer[offset]);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new PacketException("out of bound offset U8");
|
||||
}
|
||||
}
|
||||
|
||||
// will update position
|
||||
public static int readU8(ByteBuffer byteBuffer)
|
||||
throws PacketException
|
||||
{
|
||||
int u8 = readU8(byteBuffer.array(), byteBuffer.position(), byteBuffer.array().length);
|
||||
byteBuffer.position(byteBuffer.position()+1);
|
||||
return u8;
|
||||
}
|
||||
|
||||
public static v3s16 readV3S16(byte buffer[], int offset, int length)
|
||||
throws PacketException
|
||||
{
|
||||
if ( offset + 6 <= length) {
|
||||
int x = readS16(buffer, offset, length);
|
||||
int y = readS16(buffer, offset + 2, length);
|
||||
int z = readS16(buffer, offset + 4, length);
|
||||
return new v3s16(x, y, z);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new PacketException("out of bound offset v3s16");
|
||||
}
|
||||
}
|
||||
|
||||
// Zlib decompression
|
||||
public static byte[] decompress(ByteBuffer byteBuffer) throws Exception
|
||||
{
|
||||
final Inflater inflater = new Inflater(false);
|
||||
inflater.setInput(byteBuffer.array(), byteBuffer.position(), byteBuffer.limit() - byteBuffer.position());
|
||||
|
||||
try (final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(byteBuffer.limit()))
|
||||
{
|
||||
byte[] buffer = new byte[BUFFER_SIZE];
|
||||
while (!inflater.finished())
|
||||
{
|
||||
final int count = inflater.inflate(buffer);
|
||||
outputStream.write(buffer, 0, count);
|
||||
}
|
||||
|
||||
return outputStream.toByteArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
13
java/org/artisanlogiciel/games/minetest/core/v2s16.java
Normal file
13
java/org/artisanlogiciel/games/minetest/core/v2s16.java
Normal file
@@ -0,0 +1,13 @@
|
||||
package org.artisanlogiciel.games.minetest.core;
|
||||
|
||||
public class v2s16 {
|
||||
|
||||
public int X;
|
||||
public int Y;
|
||||
|
||||
public v2s16(int x, int y) {
|
||||
X = x;
|
||||
Y = y;
|
||||
}
|
||||
|
||||
}
|
||||
14
java/org/artisanlogiciel/games/minetest/core/v3s16.java
Normal file
14
java/org/artisanlogiciel/games/minetest/core/v3s16.java
Normal file
@@ -0,0 +1,14 @@
|
||||
package org.artisanlogiciel.games.minetest.core;
|
||||
|
||||
public class v3s16 {
|
||||
|
||||
public int X;
|
||||
public int Y;
|
||||
public int Z;
|
||||
|
||||
public v3s16(int x, int y, int z) {
|
||||
X = x;
|
||||
Y = y;
|
||||
Z = z;
|
||||
}
|
||||
}
|
||||
4
java/org/artisanlogiciel/games/minetest/net/Address.java
Normal file
4
java/org/artisanlogiciel/games/minetest/net/Address.java
Normal file
@@ -0,0 +1,4 @@
|
||||
package org.artisanlogiciel.games.minetest.net;
|
||||
|
||||
public class Address {
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package org.artisanlogiciel.games.minetest.net;
|
||||
|
||||
import org.artisanlogiciel.games.minetest.core.PacketException;
|
||||
import org.artisanlogiciel.games.minetest.core.Serialize;
|
||||
|
||||
public class BufferedPacket {
|
||||
|
||||
public static int BASE_HEADER_SIZE = 7;
|
||||
|
||||
// Data of the packet, including headers
|
||||
byte[] m_data;
|
||||
|
||||
BufferedPacket(byte[] data)
|
||||
{
|
||||
m_data = data;
|
||||
}
|
||||
|
||||
int getSeqNum()
|
||||
throws PacketException
|
||||
{
|
||||
return Serialize.readU16(m_data, BASE_HEADER_SIZE + 1, size());
|
||||
}
|
||||
|
||||
int size()
|
||||
{
|
||||
return m_data.length;
|
||||
}
|
||||
}
|
||||
9
java/org/artisanlogiciel/games/minetest/net/Channel.java
Normal file
9
java/org/artisanlogiciel/games/minetest/net/Channel.java
Normal file
@@ -0,0 +1,9 @@
|
||||
package org.artisanlogiciel.games.minetest.net;
|
||||
|
||||
public class Channel {
|
||||
|
||||
int readNextIncomingSeqNum()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package org.artisanlogiciel.games.minetest.net;
|
||||
|
||||
public class ConnectionEvent {
|
||||
|
||||
ConnectionEventType m_event_type;
|
||||
int m_peer_id;
|
||||
byte[] m_data;
|
||||
boolean m_timeout;
|
||||
|
||||
|
||||
ConnectionEvent(ConnectionEventType eventType)
|
||||
{
|
||||
m_event_type = eventType;
|
||||
}
|
||||
|
||||
String describe()
|
||||
{
|
||||
return m_event_type.toString();
|
||||
}
|
||||
|
||||
ConnectionEventPtr create(ConnectionEventType type)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
ConnectionEventPtr dataReceived(int peer_id, byte[] data)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
ConnectionEventPtr peerAdded(int peer_id, Address address)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
ConnectionEventPtr peerRemoved(int peer_id, boolean is_timeout, Address address)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
ConnectionEventPtr bindFailed()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package org.artisanlogiciel.games.minetest.net;
|
||||
|
||||
public class ConnectionEventPtr {
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package org.artisanlogiciel.games.minetest.net;
|
||||
|
||||
public enum ConnectionEventType {
|
||||
CONNEVENT_NONE,
|
||||
CONNEVENT_DATA_RECEIVED,
|
||||
CONNEVENT_PEER_ADDED,
|
||||
CONNEVENT_PEER_REMOVED,
|
||||
CONNEVENT_BIND_FAILED
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
package org.artisanlogiciel.games.minetest.net;
|
||||
|
||||
import org.artisanlogiciel.games.minetest.core.PacketException;
|
||||
import org.artisanlogiciel.games.minetest.core.Serialize;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class IncomingSplitBuffer {
|
||||
|
||||
int m_seqnum;
|
||||
int m_chunk_count;
|
||||
int m_chunk_num;
|
||||
|
||||
// number of chunks
|
||||
int m_got = 0;
|
||||
|
||||
NetworkPacket m_list[] = null;
|
||||
BufferedPacket m_reassembled = null;
|
||||
|
||||
BufferedPacket insert(NetworkPacket networkPacket)
|
||||
throws PacketException
|
||||
{
|
||||
|
||||
byte[] buffer = networkPacket.getBuffer();
|
||||
int length = networkPacket.getLength();
|
||||
int offset = networkPacket.getOffset();
|
||||
|
||||
int type = Serialize.readU8(buffer , offset, length);
|
||||
int seqnum = Serialize.readU16(buffer, offset + 1, length);
|
||||
// total number of chunk
|
||||
int chunk_count = Serialize.readU16(buffer, offset + 3, length);
|
||||
// this chunk number
|
||||
int chunk_num = Serialize.readU16(buffer, offset+ 5, length);
|
||||
|
||||
System.out.println("Split length " + length + " type " + type + " seqnum " + seqnum + " chunk_num/chunk_count " + chunk_num + "/" + chunk_count );
|
||||
|
||||
// move to next header
|
||||
networkPacket.addOffset(7);
|
||||
|
||||
if (m_reassembled != null)
|
||||
{
|
||||
return m_reassembled;
|
||||
}
|
||||
|
||||
if (m_list == null )
|
||||
{
|
||||
m_list = new NetworkPacket[chunk_count];
|
||||
}
|
||||
|
||||
if ( chunk_num < chunk_count && (m_list[chunk_num] == null) )
|
||||
{
|
||||
m_list[chunk_num] = networkPacket;
|
||||
m_got ++;
|
||||
}
|
||||
//todo seqnum
|
||||
m_seqnum = seqnum;
|
||||
|
||||
// fully obtained
|
||||
if ( m_got == chunk_count )
|
||||
{
|
||||
reassemble();
|
||||
}
|
||||
|
||||
return m_reassembled;
|
||||
|
||||
}
|
||||
|
||||
private void reassemble()
|
||||
{
|
||||
// keep first header since result is a valid BufferedPacket
|
||||
int fullLength = BufferedPacket.BASE_HEADER_SIZE;
|
||||
for (NetworkPacket networkPacket : m_list )
|
||||
{
|
||||
fullLength += networkPacket.getLength() - networkPacket.getOffset();
|
||||
}
|
||||
byte[] reassembled = new byte[fullLength];
|
||||
NetworkPacket first = m_list[0];
|
||||
System.arraycopy(first.getBuffer(),0,reassembled,0,BufferedPacket.BASE_HEADER_SIZE);
|
||||
int offset = BufferedPacket.BASE_HEADER_SIZE;
|
||||
for (NetworkPacket networkPacket : m_list )
|
||||
{
|
||||
int dataLength = networkPacket.getLength() - networkPacket.getOffset();
|
||||
System.arraycopy(networkPacket.getBuffer(), networkPacket.getOffset(),reassembled,offset,dataLength);
|
||||
offset+=dataLength;
|
||||
}
|
||||
m_reassembled = new BufferedPacket(reassembled);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
127
java/org/artisanlogiciel/games/minetest/net/MiM.java
Normal file
127
java/org/artisanlogiciel/games/minetest/net/MiM.java
Normal file
@@ -0,0 +1,127 @@
|
||||
package org.artisanlogiciel.games.minetest.net;
|
||||
|
||||
import org.artisanlogiciel.games.minetest.core.PacketException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.*;
|
||||
|
||||
/**
|
||||
* Man in the middle UDP
|
||||
* very simple for one client only
|
||||
*/
|
||||
|
||||
public class MiM {
|
||||
|
||||
DatagramSocket serverSocket;
|
||||
boolean running = false;
|
||||
InetSocketAddress serverAddress;
|
||||
// will be captured, incoming address & port
|
||||
SocketAddress fromClient = null;
|
||||
int localPort;
|
||||
PacketHandler handler = null;
|
||||
|
||||
public MiM(int myPort, InetSocketAddress remoteAddress)
|
||||
{
|
||||
localPort = myPort;
|
||||
serverAddress = remoteAddress;
|
||||
}
|
||||
|
||||
public void launch()
|
||||
{
|
||||
// ServerSocket socket = new ServerSocket()
|
||||
try {
|
||||
handler = new PacketHandler();
|
||||
serverSocket = new DatagramSocket(localPort);
|
||||
DatagramSocket in = serverSocket;
|
||||
SocketAddress fromServer = serverAddress;
|
||||
Thread toServer = new Thread( () -> {runFromToServer(in,fromServer);});
|
||||
running = true;
|
||||
toServer.start();
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
System.out.println("problem");
|
||||
e.printStackTrace(System.err);
|
||||
running = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void stop()
|
||||
{
|
||||
running = false;
|
||||
}
|
||||
|
||||
public void runFromToServer(DatagramSocket in, SocketAddress fromServer) {
|
||||
try {
|
||||
|
||||
while (running) {
|
||||
// quick way, a new buffer at each reception
|
||||
// to handle split packets that are buffered
|
||||
byte[] buf = new byte[4096];
|
||||
SocketAddress toRemote = null;
|
||||
DatagramPacket packet = new DatagramPacket(buf, buf.length);
|
||||
in.receive(packet);
|
||||
SocketAddress from = packet.getSocketAddress();
|
||||
if ( from.equals(fromServer)) {
|
||||
// no client yet
|
||||
if ( fromClient == null )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
fromServer(buf,packet.getLength());
|
||||
toRemote = fromClient;
|
||||
}
|
||||
else
|
||||
{
|
||||
// record client
|
||||
// later on to be smart : could try to record peer id ?
|
||||
// will add a constraint : only one auth at a time
|
||||
if ( fromClient == null ) {
|
||||
fromClient = from;
|
||||
}
|
||||
fromClient(buf,packet.getLength());
|
||||
toRemote = fromServer;
|
||||
}
|
||||
packet = new DatagramPacket(buf, packet.getLength(), toRemote);
|
||||
in.send(packet);
|
||||
}
|
||||
}
|
||||
catch( IOException ioException)
|
||||
{
|
||||
//
|
||||
System.out.println("oops");
|
||||
}
|
||||
// socket.close();
|
||||
}
|
||||
|
||||
void fromServer(byte[] buffer, int length)
|
||||
{
|
||||
try {
|
||||
// reply from server
|
||||
if (handler != null) {
|
||||
handler.fromServer(buffer, length);
|
||||
}
|
||||
}
|
||||
catch (PacketException packetException)
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
|
||||
void fromClient(byte[] buffer, int length)
|
||||
{
|
||||
try {
|
||||
// reply from client
|
||||
if (handler != null) {
|
||||
handler.fromClient(buffer, length);
|
||||
}
|
||||
}
|
||||
catch (PacketException packetException)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package org.artisanlogiciel.games.minetest.net;
|
||||
|
||||
import org.artisanlogiciel.games.minetest.core.PacketException;
|
||||
import org.artisanlogiciel.games.minetest.core.Serialize;
|
||||
import org.artisanlogiciel.games.minetest.core.v3s16;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
public class NetworkPacket {
|
||||
|
||||
int m_protocol_id;
|
||||
int m_peer_id;
|
||||
int m_channel;
|
||||
public PacketType packetType;
|
||||
|
||||
// somehow a BufferedPacket ...
|
||||
byte[] m_buffer; // m_data
|
||||
// used part in buffer
|
||||
int m_length; // m_datasize ?
|
||||
// current header index in packet
|
||||
int m_offset; // m_read_offset
|
||||
|
||||
short m_command = 0;
|
||||
|
||||
public NetworkPacket(int protocol_id, int peer_id, int channel, PacketType type, byte[] buffer, int length) {
|
||||
this.m_protocol_id = protocol_id;
|
||||
this.m_peer_id = peer_id;
|
||||
this.m_channel = channel;
|
||||
this.packetType = type;
|
||||
m_buffer = buffer;
|
||||
m_length = length;
|
||||
m_offset = 0;
|
||||
}
|
||||
|
||||
public int getChannel()
|
||||
{
|
||||
return m_channel;
|
||||
}
|
||||
|
||||
public byte[] getBuffer()
|
||||
{
|
||||
return m_buffer;
|
||||
}
|
||||
|
||||
public int getOffset() {
|
||||
return m_offset;
|
||||
}
|
||||
|
||||
public int getLength() {
|
||||
return m_length;
|
||||
}
|
||||
|
||||
void addOffset(int offset)
|
||||
{
|
||||
m_offset += offset;
|
||||
}
|
||||
|
||||
// Serialization
|
||||
public v3s16 v3s16()
|
||||
throws PacketException
|
||||
{
|
||||
return Serialize.readV3S16(m_buffer, m_offset, m_length);
|
||||
}
|
||||
|
||||
public ByteBuffer getByteBuffer()
|
||||
{
|
||||
ByteBuffer buffer = ByteBuffer.wrap(m_buffer,m_offset,m_length-m_offset);
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
287
java/org/artisanlogiciel/games/minetest/net/PacketHandler.java
Normal file
287
java/org/artisanlogiciel/games/minetest/net/PacketHandler.java
Normal file
@@ -0,0 +1,287 @@
|
||||
package org.artisanlogiciel.games.minetest.net;
|
||||
import com.github.luben.zstd.Zstd;
|
||||
import org.artisanlogiciel.games.minetest.MapBlock;
|
||||
import org.artisanlogiciel.games.minetest.MapNode;
|
||||
import org.artisanlogiciel.games.minetest.core.PacketException;
|
||||
import org.artisanlogiciel.games.minetest.core.Serialize;
|
||||
import org.artisanlogiciel.games.minetest.core.v2s16;
|
||||
import org.artisanlogiciel.games.minetest.core.v3s16;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* see src/network/networkprotocol.h of minetest sources
|
||||
*
|
||||
*
|
||||
* === NOTES ===
|
||||
*
|
||||
* A packet is sent through a channel to a peer with a basic header:
|
||||
* Header (7 bytes):
|
||||
* [0] u32 protocol_id
|
||||
* [4] session_t sender_peer_id
|
||||
* [6] u8 channel
|
||||
* sender_peer_id:
|
||||
* Unique to each peer.
|
||||
* value 0 (PEER_ID_INEXISTENT) is reserved for making new connections
|
||||
* value 1 (PEER_ID_SERVER) is reserved for server
|
||||
* these constants are defined in constants.h
|
||||
* channel:
|
||||
* Channel numbers have no intrinsic meaning. Currently only 0, 1, 2 exist.
|
||||
*
|
||||
*
|
||||
* */
|
||||
|
||||
public class PacketHandler {
|
||||
|
||||
ReliablePacketBuffer reliableBuffer = null;
|
||||
|
||||
IncomingSplitBuffer[] incomingChanneleSplitBuffer = new IncomingSplitBuffer[4];
|
||||
|
||||
// server serialization in hello version
|
||||
int ser_version;
|
||||
|
||||
// minetest protocol should be 0x4f457403
|
||||
static int readProtocolId(byte[] buffer, int length)
|
||||
throws PacketException
|
||||
{
|
||||
return Serialize.readU32(buffer, 0, length);
|
||||
}
|
||||
|
||||
static int readPeerId(byte[] buffer, int length)
|
||||
throws PacketException
|
||||
{
|
||||
return Serialize.readU16(buffer,4,length);
|
||||
}
|
||||
|
||||
static int readChannel(byte[] buffer, int length)
|
||||
throws PacketException
|
||||
{
|
||||
return Serialize.readU8(buffer, 6, length);
|
||||
}
|
||||
|
||||
NetworkPacket handleAny(byte[] buffer, int length)
|
||||
throws PacketException
|
||||
{
|
||||
int protocol_id = readProtocolId(buffer, length);
|
||||
int peer_id = readPeerId(buffer, length);
|
||||
int channel = readChannel(buffer, length);
|
||||
int type = Serialize.readU8(buffer,7,length);
|
||||
PacketType packetType = PacketType.getPacketType(type);
|
||||
|
||||
NetworkPacket networkPacket = new NetworkPacket(protocol_id,peer_id,channel,packetType, buffer, length);
|
||||
|
||||
// offset of packet within buffer.
|
||||
networkPacket.addOffset(BufferedPacket.BASE_HEADER_SIZE);
|
||||
|
||||
System.out.println("length " + length + " protocol_id " + String.format("%x", protocol_id) + " peer_id " + peer_id + " channel " + channel + " type " + packetType);
|
||||
|
||||
return networkPacket;
|
||||
}
|
||||
|
||||
void handlePacketType_Control(NetworkPacket networkPacket)
|
||||
throws PacketException {
|
||||
}
|
||||
|
||||
void handlePacketType_Reliable(NetworkPacket networkPacket)
|
||||
throws PacketException
|
||||
{
|
||||
// TODO handle misordered...
|
||||
/*
|
||||
if ( reliableBuffer == null )
|
||||
{
|
||||
reliableBuffer = new ReliablePacketBuffer();
|
||||
}
|
||||
|
||||
BufferedPacket packet = new BufferedPacket(buffer);
|
||||
int nextExpected = 0;
|
||||
reliableBuffer.insert(packet, nextExpected);
|
||||
*/
|
||||
|
||||
// handle nested packet
|
||||
networkPacket.addOffset(3);
|
||||
int offset = networkPacket.getOffset();
|
||||
byte buffer[] = networkPacket.getBuffer();
|
||||
int length = networkPacket.getLength();
|
||||
|
||||
PacketType packetType = PacketType.getPacketType(Serialize.readU8(buffer,offset,length));
|
||||
|
||||
System.out.println( "reliable " + packetType.toString());
|
||||
|
||||
switch (packetType)
|
||||
{
|
||||
case PACKET_TYPE_CONTROL:
|
||||
//
|
||||
handlePacketType_Control(networkPacket);
|
||||
break;
|
||||
case PACKET_TYPE_ORIGINAL:
|
||||
handlePacketType_Original(networkPacket);
|
||||
break;
|
||||
case PACKET_TYPE_SPLIT:
|
||||
handlePacketType_Split(networkPacket);
|
||||
break;
|
||||
case PACKET_TYPE_RELIABLE:
|
||||
// this is an error, no nested reliable accepted.
|
||||
throw new PacketException("nested reliable");
|
||||
default: // error
|
||||
throw new PacketException("unknown type");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void handleCommand_Hello(NetworkPacket networkPacket)
|
||||
throws PacketException {
|
||||
|
||||
// u8 deployed serialisation version
|
||||
ByteBuffer buffer = networkPacket.getByteBuffer();
|
||||
int ser_ver = Serialize.readU8(buffer);
|
||||
ser_version = ser_ver;
|
||||
// u16 deployed network compression mode
|
||||
int net_compress = Serialize.readU16(buffer);
|
||||
// u16 deployed protocol version
|
||||
int deployed = Serialize.readU16(buffer);
|
||||
System.out.println(String.format("HELLO ser_ver=%d net_compress %d deployed %d", ser_ver,net_compress,deployed));
|
||||
// u32 supported auth methods
|
||||
// std::string username that should be used for legacy hash (for proper casing)
|
||||
|
||||
}
|
||||
|
||||
// clientpackethandler.cpp Client::handleCommand_BlockData(NetworkPacket* pkt)
|
||||
// TOCLIENT_BLOCKDATA
|
||||
void handleCommand_BlockData(NetworkPacket networkPacket)
|
||||
throws PacketException
|
||||
{
|
||||
v3s16 p = networkPacket.v3s16();
|
||||
|
||||
// will be used to get vector...
|
||||
// v2s16 p2d = new v2s16(p.X,p.Z);
|
||||
|
||||
System.out.println(String.format(" (X,Y,Z) %d %d %d", p.X, p.Y,p.Z));
|
||||
//
|
||||
networkPacket.addOffset(6);
|
||||
|
||||
// FIXME get it from handshake
|
||||
int version = 28;
|
||||
|
||||
// Should be a MapBlock
|
||||
MapBlock block = new MapBlock();
|
||||
block.deSerialize(networkPacket.getByteBuffer(),version);
|
||||
|
||||
// now we have a block ! what to do with it ?
|
||||
}
|
||||
|
||||
void handlePacketCommand(NetworkPacket networkPacket)
|
||||
throws PacketException {
|
||||
|
||||
// consume type and ??? well ... 2
|
||||
int command = Serialize.readU16(networkPacket.getBuffer(),networkPacket.getOffset(),networkPacket.getLength());
|
||||
|
||||
System.out.println(String.format("command %x length %d", command, networkPacket.getLength()));
|
||||
|
||||
networkPacket.addOffset(2);
|
||||
// Original ... toClient - toServer
|
||||
switch (command)
|
||||
{
|
||||
case 0x02:
|
||||
{
|
||||
System.out.println("TOCLIENT_HELLO");
|
||||
handleCommand_Hello(networkPacket);
|
||||
}
|
||||
break;
|
||||
case 0x20:
|
||||
{
|
||||
System.out.println("TOCLIENT_BLOCKDATA");
|
||||
handleCommand_BlockData(networkPacket);
|
||||
}
|
||||
break;
|
||||
case 0x4f:
|
||||
{
|
||||
//TOCLIENT_SET_SKY = 0x4f,
|
||||
System.out.println("TOCLIENT_SET_SKY");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void handlePacketType_Original(NetworkPacket networkPacket)
|
||||
throws PacketException {
|
||||
|
||||
// eat original type 1.
|
||||
networkPacket.addOffset(1);
|
||||
handlePacketCommand(networkPacket);
|
||||
}
|
||||
|
||||
void handlePacketType_Split(NetworkPacket networkPacket)
|
||||
throws PacketException {
|
||||
|
||||
int channel = networkPacket.getChannel();
|
||||
if ( channel < incomingChanneleSplitBuffer.length) {
|
||||
IncomingSplitBuffer splitBuffer = incomingChanneleSplitBuffer[channel];
|
||||
if ( splitBuffer == null )
|
||||
{
|
||||
splitBuffer = new IncomingSplitBuffer();
|
||||
incomingChanneleSplitBuffer[channel] = splitBuffer;
|
||||
}
|
||||
if ( splitBuffer != null ) {
|
||||
BufferedPacket bufferedPacket = splitBuffer.insert(networkPacket);
|
||||
if ( bufferedPacket != null ) {
|
||||
// well should handle it.
|
||||
System.out.println("Reassembled packet size " + bufferedPacket.m_data.length);
|
||||
// reset it.
|
||||
incomingChanneleSplitBuffer[channel] = new IncomingSplitBuffer();
|
||||
handleBufferedPacket(bufferedPacket);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new PacketException("invalid channel " + channel);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
void handleBufferedPacket(BufferedPacket bufferedPacket)
|
||||
throws PacketException
|
||||
{
|
||||
// FIXME, why a BufferedPacket anyway since we finaly need a NetworkPacket ?
|
||||
NetworkPacket networkPacket = new NetworkPacket(0,0,0, PacketType.PACKET_TYPE_ORIGINAL,bufferedPacket.m_data, bufferedPacket.size());
|
||||
networkPacket.addOffset(BufferedPacket.BASE_HEADER_SIZE);
|
||||
handlePacketCommand(networkPacket);
|
||||
}
|
||||
|
||||
void fromServer(byte[] buffer, int length)
|
||||
throws PacketException
|
||||
{
|
||||
System.out.print(" <- ");
|
||||
NetworkPacket networkPacket = handleAny(buffer,length);
|
||||
|
||||
switch (networkPacket.packetType)
|
||||
{
|
||||
case PACKET_TYPE_CONTROL:
|
||||
//
|
||||
handlePacketType_Control(networkPacket);
|
||||
break;
|
||||
case PACKET_TYPE_RELIABLE:
|
||||
//
|
||||
handlePacketType_Reliable(networkPacket);
|
||||
break;
|
||||
case PACKET_TYPE_ORIGINAL:
|
||||
handlePacketType_Original(networkPacket);
|
||||
break;
|
||||
case PACKET_TYPE_SPLIT:
|
||||
handlePacketType_Split(networkPacket);
|
||||
break;
|
||||
default: // error
|
||||
throw new PacketException("unknown type");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void fromClient(byte[] buffer, int length)
|
||||
throws PacketException
|
||||
{
|
||||
System.out.print(" -> ");
|
||||
handleAny(buffer,length);
|
||||
}
|
||||
|
||||
}
|
||||
27
java/org/artisanlogiciel/games/minetest/net/PacketType.java
Normal file
27
java/org/artisanlogiciel/games/minetest/net/PacketType.java
Normal file
@@ -0,0 +1,27 @@
|
||||
package org.artisanlogiciel.games.minetest.net;
|
||||
|
||||
public enum PacketType {
|
||||
PACKET_TYPE_CONTROL,
|
||||
PACKET_TYPE_ORIGINAL,
|
||||
PACKET_TYPE_SPLIT,
|
||||
PACKET_TYPE_RELIABLE,
|
||||
PACKET_TYPE_ERROR;
|
||||
|
||||
static PacketType getPacketType(int value)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case 0:
|
||||
return PACKET_TYPE_CONTROL;
|
||||
case 1:
|
||||
return PACKET_TYPE_ORIGINAL;
|
||||
case 2:
|
||||
return PACKET_TYPE_SPLIT;
|
||||
case 3:
|
||||
return PACKET_TYPE_RELIABLE;
|
||||
default:
|
||||
return PACKET_TYPE_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package org.artisanlogiciel.games.minetest.net;
|
||||
|
||||
import org.artisanlogiciel.games.minetest.core.PacketException;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public class ReliablePacketBuffer {
|
||||
|
||||
List<BufferedPacket> m_list = new LinkedList<>();
|
||||
|
||||
void insert(BufferedPacket packet, int nextExpected)
|
||||
throws PacketException
|
||||
{
|
||||
//
|
||||
int seqNum = packet.getSeqNum();
|
||||
m_list.add(packet);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,5 +3,5 @@ project_default=dist
|
||||
project_basedir=$(pwd)
|
||||
project_mainpackage=org.artisanlogiciel.games
|
||||
project_mainclass=$project_mainpackage.maze.gui.Display
|
||||
project_version=1.1
|
||||
project_version=1.2
|
||||
default_args='lab/lab30x30.raw'
|
||||
|
||||
Reference in New Issue
Block a user