bluefire
Version:
Lightweight tcp framework
163 lines (122 loc) • 4.89 kB
text/coffeescript
Promise = require("promise")
Packet = require("./Packet")
module.exports = class Parser
constructor: (@isServer) ->
@head = new Packet("Head") # register empty head
@serverPackets = { }
@clientPackets = { }
@packetConditions = { } # condition - name map
initialize: (conditionField = "opcode") =>
@conditionField = conditionField
###
@return [Packet] packet representing head for all packets
###
getHead: () =>
return @head
###
sets the current head to the given
@param head [Packet] packet to be given as a head packet
@return [Packet] new head packet instance
###
setHead: (head) =>
@head = head
return @head
###
Sets the condition field to the custom one
@param conditionField[String] name of condition field in packet
###
setConditionField:(@conditionField) =>
###
Registers packet by given name, location and condition
###
registerPacket: (packet, isServerPacket, condition = null) =>
if isServerPacket # switch between server and client packets
@serverPackets[packet.name] = packet
else
@clientPackets[packet.name] = packet
# register condition for current packet
if (@isServer and not isServerPacket) or (not @isServer and isServerPacket)
@registerCondition(packet.name, condition)
else
packet.addPredefinedValue(@conditionField, condition) # adds as predefined value
return packet
packet: (name, isServerPacket, structure) =>
condition = @findCondition(structure) # get additional condition
packet = new Packet(name, @head)
packet.add(structure)
return @registerPacket(packet, isServerPacket, condition)
###
Finds condition field value in the packet structure
@param structure [Array] an array of structured for the packet
@return [String|Integer|Null] value of condition field or null if not found
###
findCondition: (structure) ->
for field in structure
for name, value of field
if name is @conditionField
return value
return null
###
Returns packet from collection by given type
@param packetName [String] packet name from collection
@param isServer [Boolean] true if server packet is needed, else false. Default: true
###
getPacket: (packetName, isServer = true) =>
return if isServer then @serverPackets[packetName] else @clientPackets[packetName]
registerCondition: (packetName, condition = null) ->
if condition? and packetName?
@packetConditions[condition] = packetName
###
Parses the given data buffer into the structure which
represents the given packet by conditionField from head
@param buffer [Buffer] buffer to be parsed
@param callback [Function(name,data)] callback to be called after parse
@param packetname [String] optional packet name if already known
###
parse: (buffer, packetName = null) =>
return new Promise (fulfill, reject) =>
parsedData = { }
head = @getHead()
index = 0 # current byte index for parser
# read whole head
for parser in head.packetParseData
name = parser["name"]
read = parser["read"]
[parsedData[name], index] = read(buffer, index)
# parse packets that are oposite
name = if packetName? then packetName else @packetConditions[parsedData[@conditionField]]
packet = @getPacket(name, !@isServer)
if not packet?
reject(new Error("Packet not found"))
return
for parser in packet.packetParseData
name = parser["name"]
read = parser["read"]
[parsedData[name], index] = read(buffer, index)
fulfill({name: packet.name, data: parsedData})
###
Creates byte buffer that can be passed right into socket with current
packet structure
@param data [Object] data to be serialized
@param packetName [String] name of packet to serialize
@param callback [Function] function to be called
@example Serialization of previously registered packet with name 'myPacket'
serialize { myInt : 5}, 'myPacket', (buffer) ->
#send buffer or do something else
###
serialize: (data, packetName) =>
return new Promise (fulfill, reject) =>
# serialize packets from this side
packet = @getPacket(packetName, @isServer)
bufferArray = []
for parser in packet.head.packetParseData
name = parser['name']
write = parser['write']
data[name] = packet.predefinedValues[name] if not data[name]? and packet.predefinedValues[name]?
bufferArray.push(write(data[name]))
for parser in packet.packetParseData
name = parser['name']
write = parser['write']
data[name] = packet.predefinedValues[name] if not data[name]? and packet.predefinedValues[name]?
bufferArray.push(write(data[name]))
fulfill(Buffer.concat(bufferArray)) # glue up whole array of buffers