[common] bullet with bricks collision
This commit is contained in:
60
src/common/haxe/ru/m/connect/BaseConnection.hx
Executable file
60
src/common/haxe/ru/m/connect/BaseConnection.hx
Executable file
@@ -0,0 +1,60 @@
|
||||
package ru.m.connect;
|
||||
|
||||
import haxework.dispath.Dispatcher;
|
||||
import haxework.dispath.IDispatcher;
|
||||
import haxework.provider.Provider;
|
||||
import haxe.io.Bytes;
|
||||
import protohx.Message;
|
||||
import ru.m.connect.IConnection;
|
||||
|
||||
class BaseConnection implements IConnection {
|
||||
public var handler(default,default):IDispatcher<IConnectionHandler>;
|
||||
public var packetHandler(default,default):IDispatcher<IPacketHandler>;
|
||||
public var connected(default, null):Bool;
|
||||
public var queue(default, null):PacketQueue;
|
||||
public var builder(default, null):IPacketBuilder;
|
||||
|
||||
public function new() {
|
||||
this.builder = Provider.get(IPacketBuilder);
|
||||
this.queue = new PacketQueue(builder);
|
||||
this.handler = new Dispatcher<IConnectionHandler>();
|
||||
this.packetHandler = new Dispatcher<IPacketHandler>();
|
||||
}
|
||||
|
||||
public function connect():Void {
|
||||
throw "Not implemented";
|
||||
}
|
||||
|
||||
public function disconnect():Void {
|
||||
throw "Not implemented";
|
||||
}
|
||||
|
||||
public function pushData(bytes:Bytes):Void {
|
||||
queue.addBytes(bytes);
|
||||
while (queue.hasMsg()) {
|
||||
var packet:Message = queue.popMsg();
|
||||
try {
|
||||
receive(packet);
|
||||
} catch (error:Dynamic) {
|
||||
trace(error);
|
||||
handler.dispatch(function(h) h.onError(error));
|
||||
}
|
||||
}
|
||||
}
|
||||
public function send(packet:Message):Void {
|
||||
#if proto_debug L.d("Send", Type.getClassName(Type.getClass(packet)).split(".").pop()); #end
|
||||
}
|
||||
|
||||
public function receive(packet:Message):Void {
|
||||
#if proto_debug L.d("Receive", Type.getClassName(Type.getClass(packet)).split(".").pop()); #end
|
||||
var name = "on" + Type.getClassName(Type.getClass(packet)).split(".").pop();
|
||||
packetHandler.dispatch(function(h) {
|
||||
var method = Reflect.field(h, name);
|
||||
if (method != null && Reflect.isFunction(method)) {
|
||||
Reflect.callMethod(h, method, [packet]);
|
||||
} else {
|
||||
h.onPacket(packet);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
40
src/common/haxe/ru/m/connect/IConnection.hx
Executable file
40
src/common/haxe/ru/m/connect/IConnection.hx
Executable file
@@ -0,0 +1,40 @@
|
||||
package ru.m.connect;
|
||||
|
||||
import haxework.dispath.IDispatcher;
|
||||
import haxe.io.Bytes;
|
||||
import protohx.Message;
|
||||
|
||||
interface IConnection {
|
||||
public var connected(default,null):Bool;
|
||||
public var handler(default,default):IDispatcher<IConnectionHandler>;
|
||||
public var packetHandler(default,default):IDispatcher<IPacketHandler>;
|
||||
|
||||
public var builder(default,null):IPacketBuilder;
|
||||
|
||||
public function connect():Void;
|
||||
public function disconnect():Void;
|
||||
public function send(packet:Message):Void;
|
||||
public function pushData(bytes:Bytes):Void;
|
||||
|
||||
public function receive(packet:Message):Void;
|
||||
}
|
||||
|
||||
interface IConnectionHandler {
|
||||
public function onConnected():Void;
|
||||
public function onDisconnected():Void;
|
||||
public function onError(error:Dynamic):Void;
|
||||
}
|
||||
|
||||
interface IPacketHandler {
|
||||
public function onPacket(packet:Message):Void;
|
||||
}
|
||||
|
||||
typedef PacketMeta = {
|
||||
var family:Int;
|
||||
var id:Int;
|
||||
}
|
||||
|
||||
interface IPacketBuilder {
|
||||
public function packetMeta(packet:Message):PacketMeta;
|
||||
public function buildPacket(meta:PacketMeta):Message;
|
||||
}
|
||||
80
src/common/haxe/ru/m/connect/PacketQueue.hx
Executable file
80
src/common/haxe/ru/m/connect/PacketQueue.hx
Executable file
@@ -0,0 +1,80 @@
|
||||
package ru.m.connect;
|
||||
|
||||
import ru.m.connect.IConnection.IPacketBuilder;
|
||||
import protohx.Message;
|
||||
import haxe.io.BytesInput;
|
||||
import haxe.io.BytesBuffer;
|
||||
import haxe.io.Bytes;
|
||||
|
||||
class PacketQueue {
|
||||
|
||||
public static inline var HEADER_SIZE:Int = 4;
|
||||
|
||||
private var builder:IPacketBuilder;
|
||||
private var bytesBuff:Bytes;
|
||||
private var msgs:List<Message>;
|
||||
|
||||
public function new(builder:IPacketBuilder) {
|
||||
this.builder = builder;
|
||||
msgs = new List<Message>();
|
||||
}
|
||||
|
||||
public inline function hasMsg():Bool {
|
||||
return !msgs.isEmpty();
|
||||
}
|
||||
|
||||
public inline function popMsg():Message {
|
||||
return msgs.pop();
|
||||
}
|
||||
|
||||
public inline function addMsg(msg:Message):Void {
|
||||
msgs.add(msg);
|
||||
}
|
||||
|
||||
public function addBytes(bytes:Bytes) {
|
||||
if (bytes == null) {
|
||||
return;
|
||||
}
|
||||
if (bytesBuff == null) {
|
||||
bytesBuff = bytes;
|
||||
} else {
|
||||
var buffer = new BytesBuffer();
|
||||
buffer.add(bytesBuff);
|
||||
buffer.add(bytes);
|
||||
bytesBuff = buffer.getBytes();
|
||||
}
|
||||
if (bytesBuff == null || bytesBuff.length < HEADER_SIZE) {
|
||||
return;
|
||||
}
|
||||
var available = bytesBuff.length;
|
||||
var bi = new BytesInput(bytesBuff);
|
||||
bi.bigEndian = false;
|
||||
while (available >= HEADER_SIZE) {
|
||||
var family = bi.readByte();
|
||||
var id = bi.readByte();
|
||||
var packetSize = bi.readUInt16();
|
||||
available -= HEADER_SIZE;
|
||||
if (packetSize <= available) {
|
||||
available -= packetSize;
|
||||
var msgBytes = bi.read(packetSize);
|
||||
var packet = builder.buildPacket({family:family, id:id});
|
||||
packet.mergeFrom(msgBytes);
|
||||
addMsg(packet);
|
||||
} else {
|
||||
available += HEADER_SIZE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (available == 0) {
|
||||
bytesBuff = null;
|
||||
} else if (available > 0) {
|
||||
if (bytesBuff.length != available) {
|
||||
var pos = bytesBuff.length - available;
|
||||
bytesBuff = bytesBuff.sub(pos, available);
|
||||
}
|
||||
} else {
|
||||
throw "Wrong available: " + available;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
31
src/common/haxe/ru/m/connect/WebSocketTools.hx
Normal file
31
src/common/haxe/ru/m/connect/WebSocketTools.hx
Normal file
@@ -0,0 +1,31 @@
|
||||
package ru.m.connect;
|
||||
|
||||
import haxe.io.Bytes;
|
||||
import haxe.io.BytesOutput;
|
||||
import ru.m.connect.IConnection;
|
||||
import protohx.Message;
|
||||
|
||||
class WebSocketTools {
|
||||
|
||||
public static function packet2string(packet:Message, builder:IPacketBuilder):String {
|
||||
var meta = builder.packetMeta(packet);
|
||||
var b = new BytesOutput();
|
||||
packet.writeTo(b);
|
||||
var data = b.getBytes();
|
||||
var res = new BytesOutput();
|
||||
res.writeByte(meta.family);
|
||||
res.writeByte(meta.id);
|
||||
//res.writeUInt16(data.length);
|
||||
res.write(data);
|
||||
return Base64.encodeBase64(res.getBytes());
|
||||
}
|
||||
|
||||
public static function string2packet(data:String, builder:IPacketBuilder):Message {
|
||||
var bytes = Base64.decodeBase64(data);
|
||||
var family = bytes.get(0);
|
||||
var id = bytes.get(1);
|
||||
var packet = builder.buildPacket({family:family, id:id});
|
||||
packet.mergeFrom(bytes.sub(2, bytes.length - 2));
|
||||
return packet;
|
||||
}
|
||||
}
|
||||
87
src/common/haxe/ru/m/connect/flash/FlashConnection.hx
Executable file
87
src/common/haxe/ru/m/connect/flash/FlashConnection.hx
Executable file
@@ -0,0 +1,87 @@
|
||||
package ru.m.connect.flash;
|
||||
|
||||
import ru.m.connect.IConnection.IConnectionHandler;
|
||||
import flash.utils.Endian;
|
||||
import haxe.io.BytesOutput;
|
||||
import protohx.Message;
|
||||
import haxe.io.Bytes;
|
||||
import flash.events.ErrorEvent;
|
||||
import flash.events.ProgressEvent;
|
||||
import flash.events.Event;
|
||||
import flash.events.SecurityErrorEvent;
|
||||
import flash.events.IOErrorEvent;
|
||||
import flash.net.Socket;
|
||||
|
||||
class FlashConnection extends BaseConnection {
|
||||
|
||||
private var host:String;
|
||||
private var port:Int;
|
||||
private var socket:Socket;
|
||||
|
||||
public function new(host:String, port:Int) {
|
||||
super();
|
||||
this.host = host;
|
||||
this.port = port;
|
||||
connected = false;
|
||||
socket = new Socket();
|
||||
socket.addEventListener(IOErrorEvent.IO_ERROR, onError);
|
||||
socket.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onError);
|
||||
socket.addEventListener(Event.CLOSE, onClose);
|
||||
socket.addEventListener(Event.CONNECT, onConnect);
|
||||
socket.addEventListener(ProgressEvent.SOCKET_DATA, onSocketData);
|
||||
socket.endian = Endian.LITTLE_ENDIAN;
|
||||
}
|
||||
|
||||
override public function connect():Void {
|
||||
socket.connect(host, port);
|
||||
}
|
||||
|
||||
override public function disconnect():Void {
|
||||
if (socket.connected) {
|
||||
socket.close();
|
||||
connected = false;
|
||||
handler.dispatch(function(h) h.onDisconnected());
|
||||
}
|
||||
}
|
||||
|
||||
private function onError(event:ErrorEvent):Void {
|
||||
socket.close();
|
||||
connected = false;
|
||||
handler.dispatch(function(h) h.onError(event));
|
||||
}
|
||||
|
||||
private function onConnect(_):Void {
|
||||
connected = true;
|
||||
handler.dispatch(function(h) h.onConnected());
|
||||
}
|
||||
|
||||
private function onClose(_):Void {
|
||||
socket.close();
|
||||
connected = false;
|
||||
handler.dispatch(function(h) h.onDisconnected());
|
||||
}
|
||||
|
||||
private function onSocketData(_):Void {
|
||||
try {
|
||||
var b = new flash.utils.ByteArray();
|
||||
socket.readBytes(b);
|
||||
var bs = Bytes.ofData(cast b);
|
||||
pushData(bs);
|
||||
} catch (error:Dynamic) {
|
||||
handler.dispatch(function(h) h.onError(error));
|
||||
}
|
||||
}
|
||||
|
||||
override public function send(packet:Message):Void {
|
||||
super.send(packet);
|
||||
var meta = builder.packetMeta(packet);
|
||||
socket.writeByte(meta.family);
|
||||
socket.writeByte(meta.id);
|
||||
var out = new BytesOutput();
|
||||
packet.writeTo(out);
|
||||
var bytes = out.getBytes();
|
||||
socket.writeShort(bytes.length);
|
||||
socket.writeBytes(cast bytes.getData());
|
||||
socket.flush();
|
||||
}
|
||||
}
|
||||
82
src/common/haxe/ru/m/connect/js/JsConnection.hx
Normal file
82
src/common/haxe/ru/m/connect/js/JsConnection.hx
Normal file
@@ -0,0 +1,82 @@
|
||||
package ru.m.connect.js;
|
||||
|
||||
import ru.m.core.Base64;
|
||||
import ru.m.connect.IConnection.IConnectionHandler;
|
||||
import protohx.Message;
|
||||
import haxe.io.Bytes;
|
||||
|
||||
typedef WebSocket = {
|
||||
var onopen:Dynamic->Void;
|
||||
var onclose:Dynamic->Void;
|
||||
var onmessage:Dynamic->Void;
|
||||
var onerror:Dynamic->Void;
|
||||
function send(message:String):Void;
|
||||
function emit(a:String, b:String):Void;
|
||||
function close():Void;
|
||||
}
|
||||
|
||||
class JsConnection extends BaseConnection {
|
||||
|
||||
private var host:String;
|
||||
private var port:Int;
|
||||
private var socket:WebSocket;
|
||||
|
||||
public function new(host:String, port:Int) {
|
||||
super();
|
||||
this.host = host;
|
||||
this.port = port;
|
||||
connected = false;
|
||||
}
|
||||
|
||||
private function buildSocket(host:String, port:Int):WebSocket {
|
||||
return untyped __js__("new WebSocket('ws://'+host+':'+port);");
|
||||
}
|
||||
|
||||
override public function connect():Void {
|
||||
var self = this;
|
||||
var decodeBytes = Base64.decodeBase64;
|
||||
socket = buildSocket(host, port);
|
||||
socket.onopen = this.onConnect;
|
||||
socket.onclose = this.onClose;
|
||||
socket.onerror = this.onError;
|
||||
socket.onmessage = this.onSocketData;
|
||||
}
|
||||
|
||||
override public function disconnect():Void {
|
||||
socket.close();
|
||||
connected = false;
|
||||
handler.dispatch(function(h) h.onDisconnected());
|
||||
}
|
||||
|
||||
private function onError(event:Dynamic):Void {
|
||||
socket.close();
|
||||
connected = false;
|
||||
handler.dispatch(function(h) h.onError(event));
|
||||
}
|
||||
|
||||
private function onConnect(_):Void {
|
||||
connected = true;
|
||||
handler.dispatch(function(h) h.onConnected());
|
||||
}
|
||||
|
||||
private function onClose(_):Void {
|
||||
socket.close();
|
||||
connected = false;
|
||||
handler.dispatch(function(h) h.onDisconnected());
|
||||
}
|
||||
|
||||
private function onSocketData(event:Dynamic):Void {
|
||||
try {
|
||||
var data:String = event.data;
|
||||
var packet = WebSocketTools.string2packet(data, builder);
|
||||
receive(packet);
|
||||
} catch (error:Dynamic) {
|
||||
handler.dispatch(function(h) h.onError(error));
|
||||
}
|
||||
}
|
||||
|
||||
override public function send(packet:Message):Void {
|
||||
super.send(packet);
|
||||
socket.send(WebSocketTools.packet2string(packet, builder));
|
||||
}
|
||||
}
|
||||
38
src/common/haxe/ru/m/connect/neko/NekoConnection.hx
Executable file
38
src/common/haxe/ru/m/connect/neko/NekoConnection.hx
Executable file
@@ -0,0 +1,38 @@
|
||||
package ru.m.connect.neko;
|
||||
|
||||
import haxe.io.BytesOutput;
|
||||
import protohx.Message;
|
||||
import haxe.io.Bytes;
|
||||
import sys.net.Socket;
|
||||
import ru.m.connect.IConnection;
|
||||
|
||||
class NekoConnection extends BaseConnection {
|
||||
|
||||
public var socket(default, null):Socket;
|
||||
|
||||
public function new(socket:Socket) {
|
||||
super();
|
||||
this.socket = socket;
|
||||
socket.setFastSend(true);
|
||||
socket.output.bigEndian = false;
|
||||
socket.input.bigEndian = false;
|
||||
}
|
||||
|
||||
override public function send(packet:Message):Void {
|
||||
super.send(packet);
|
||||
try {
|
||||
var meta = builder.packetMeta(packet);
|
||||
var b = new BytesOutput();
|
||||
packet.writeTo(b);
|
||||
var bytes = b.getBytes();
|
||||
socket.output.writeByte(meta.family);
|
||||
socket.output.writeByte(meta.id);
|
||||
socket.output.writeUInt16(bytes.length);
|
||||
socket.output.write(bytes);
|
||||
socket.output.flush();
|
||||
} catch (e:Dynamic) {
|
||||
trace("Error send packet: " + Type.getClassName(Type.getClass(packet)));
|
||||
trace(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
201
src/common/haxe/ru/m/connect/neko/NekoWebConnection.hx
Normal file
201
src/common/haxe/ru/m/connect/neko/NekoWebConnection.hx
Normal file
@@ -0,0 +1,201 @@
|
||||
package ru.m.connect.neko;
|
||||
|
||||
import haxe.crypto.BaseCode;
|
||||
import haxe.crypto.Sha1;
|
||||
import protohx.Message;
|
||||
import haxe.io.Bytes;
|
||||
import sys.net.Socket;
|
||||
import ru.m.connect.IConnection;
|
||||
|
||||
class NekoWebConnection extends NekoConnection {
|
||||
|
||||
private var opened:Bool;
|
||||
|
||||
public function new(socket:Socket) {
|
||||
super(socket);
|
||||
opened = false;
|
||||
}
|
||||
|
||||
override public function send(packet:Message):Void {
|
||||
#if proto_debug L.d("Send", Type.getClassName(Type.getClass(packet)).split(".").pop()); #end
|
||||
try {
|
||||
var data = WebSocketTools.packet2string(packet, builder);
|
||||
writeData(data, socket);
|
||||
} catch (e:Dynamic) {
|
||||
trace(e);
|
||||
}
|
||||
}
|
||||
|
||||
override public function pushData(bytes:Bytes):Void {
|
||||
if (!opened) {
|
||||
var str:String = bytes.getString(0, bytes.length);
|
||||
if (StringTools.startsWith(str, "GET")) {
|
||||
var r = ~/Sec-WebSocket-Key:\s*([A-z0-9=+\/]+)/;
|
||||
r.match(str);
|
||||
opened = true;
|
||||
sendServerHandShake(socket, r.matched(1));
|
||||
}
|
||||
} else {
|
||||
var data = parseData(bytes);
|
||||
if (data != null) {
|
||||
var packet = WebSocketTools.string2packet(data, builder);
|
||||
receive(packet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private function encodeBase64(content:String):String {
|
||||
var suffix = switch (content.length % 3)
|
||||
{
|
||||
case 2: "=";
|
||||
case 1: "==";
|
||||
default: "";
|
||||
};
|
||||
return BaseCode.encode(content, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/") + suffix;
|
||||
}
|
||||
|
||||
private function hex2data(hex:String):String {
|
||||
var data = "";
|
||||
for (i in 0...Std.int(hex.length / 2)) {
|
||||
data += String.fromCharCode(Std.parseInt("0x" + hex.substr(i * 2, 2)));
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
private function sendServerHandShake(socket:sys.net.Socket, inpKey:String) {
|
||||
var outKey = encodeBase64(hex2data(Sha1.encode(StringTools.trim(inpKey) + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11")));
|
||||
|
||||
var s = "HTTP/1.1 101 Switching Protocols\r\n"
|
||||
+ "Upgrade: websocket\r\n"
|
||||
+ "Connection: Upgrade\r\n"
|
||||
+ "Sec-WebSocket-Accept: " + outKey + "\r\n"
|
||||
+ "\r\n";
|
||||
|
||||
socket.output.writeString(s);
|
||||
}
|
||||
|
||||
private function writeData(data:String, socket:sys.net.Socket, isServer = true):Void {
|
||||
socket.output.writeByte(0x81);
|
||||
|
||||
var len = 0;
|
||||
if (data.length < 126) len = data.length;
|
||||
else if (data.length < 65536) len = 126;
|
||||
else len = 127;
|
||||
|
||||
socket.output.writeByte(len | (!isServer ? 0x80 : 0x00));
|
||||
|
||||
if (data.length >= 126)
|
||||
{
|
||||
if (data.length < 65536)
|
||||
{
|
||||
socket.output.writeByte((data.length >> 8) & 0xFF);
|
||||
socket.output.writeByte(data.length & 0xFF);
|
||||
}
|
||||
else
|
||||
{
|
||||
socket.output.writeByte((data.length >> 24) & 0xFF);
|
||||
socket.output.writeByte((data.length >> 16) & 0xFF);
|
||||
socket.output.writeByte((data.length >> 8) & 0xFF);
|
||||
socket.output.writeByte(data.length & 0xFF);
|
||||
}
|
||||
}
|
||||
|
||||
if (isServer)
|
||||
{
|
||||
socket.output.writeString(data);
|
||||
}
|
||||
else
|
||||
{
|
||||
var mask = [ Std.random(256), Std.random(256), Std.random(256), Std.random(256) ];
|
||||
socket.output.writeByte(mask[0]);
|
||||
socket.output.writeByte(mask[1]);
|
||||
socket.output.writeByte(mask[2]);
|
||||
socket.output.writeByte(mask[3]);
|
||||
var maskedData = new StringBuf();
|
||||
for (i in 0...data.length)
|
||||
{
|
||||
maskedData.addChar(data.charCodeAt(i) ^ mask[i % 4]);
|
||||
}
|
||||
socket.output.writeString(maskedData.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private function parseData(bytes:Bytes):String {
|
||||
var p = 0;
|
||||
var opcode = bytes.get(p++);
|
||||
|
||||
if (opcode == 0x00)
|
||||
{
|
||||
var s = "";
|
||||
var b : Int;
|
||||
while ((b = bytes.get(p++)) != 0xFF)
|
||||
{
|
||||
s += String.fromCharCode(b);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
if (opcode == 0x81) // 0x81 = fin & text
|
||||
{
|
||||
var len = bytes.get(p++);
|
||||
|
||||
if (len & 0x80 != 0) // mask
|
||||
{
|
||||
len &= 0x7F;
|
||||
|
||||
if (len == 126)
|
||||
{
|
||||
var b2 = bytes.get(p++);
|
||||
var b3 = bytes.get(p++);
|
||||
len = (b2 << 8) + b3;
|
||||
}
|
||||
else
|
||||
if (len == 127)
|
||||
{
|
||||
var b2 = bytes.get(p++);
|
||||
var b3 = bytes.get(p++);
|
||||
var b4 = bytes.get(p++);
|
||||
var b5 = bytes.get(p++);
|
||||
len = (b2 << 24) + (b3 << 16) + (b4 << 8) + b5;
|
||||
}
|
||||
|
||||
//Lib.println("len = " + len);
|
||||
|
||||
// direct array init not work corectly!
|
||||
var mask = [];
|
||||
mask.push(bytes.get(p++));
|
||||
mask.push(bytes.get(p++));
|
||||
mask.push(bytes.get(p++));
|
||||
mask.push(bytes.get(p++));
|
||||
|
||||
//Lib.println("mask = " + mask);
|
||||
|
||||
var data = new StringBuf();
|
||||
for (i in 0...len)
|
||||
{
|
||||
data.addChar(bytes.get(p++) ^ mask[i % 4]);
|
||||
}
|
||||
|
||||
//Lib.println("readed = " + data.toString());
|
||||
return data.toString();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw "Expected masked data.";
|
||||
}
|
||||
}
|
||||
|
||||
if (opcode == 136) {
|
||||
//socket.close();
|
||||
opened = false;
|
||||
return null;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
throw "Unsupported websocket opcode: " + opcode;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user