client.lua 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. local BASE = (...):match("(.-)[^%.]+$")
  2. local socket = require("socket")
  3. local User = require( BASE .. "user" )
  4. local CMD = require( BASE .. "commands" )
  5. local utility = require( BASE .. "utility" )
  6. local Client = {}
  7. Client.__index = Client
  8. local userList = {}
  9. local numberOfUsers = 0
  10. local partMessage = ""
  11. local messageLength = nil
  12. function Client:new( address, port, playerName, authMsg )
  13. local o = {}
  14. setmetatable( o, self )
  15. authMsg = authMsg or ""
  16. print("[NET] Initialising Client...")
  17. o.conn = socket.tcp()
  18. o.conn:settimeout(5)
  19. local ok, msg = o.conn:connect( address, port )
  20. --ok, o.conn = pcall(o.conn.connect, o.conn, address, port)
  21. if ok and o.conn then
  22. o.conn:settimeout(0)
  23. self.send( o, CMD.AUTHORIZATION_REQUREST, authMsg )
  24. print("[NET] -> Client connected", o.conn)
  25. else
  26. o.conn = nil
  27. return nil
  28. end
  29. o.callbacks = {
  30. authorized = nil,
  31. received = nil,
  32. connected = nil,
  33. disconnected = nil,
  34. otherUserConnected = nil,
  35. otherUserDisconnected = nil,
  36. customDataChanged = nil,
  37. }
  38. userList = {}
  39. partMessage = ""
  40. o.clientID = nil
  41. o.playerName = playerName
  42. numberOfUsers = 0
  43. -- Filled if user is kicked:
  44. o.kickMsg = ""
  45. return o
  46. end
  47. function Client:update( dt )
  48. if self.conn then
  49. local data, msg, partOfLine = self.conn:receive( 9999 )
  50. if data then
  51. partMessage = partMessage .. data
  52. else
  53. if msg == "timeout" then
  54. if #partOfLine > 0 then
  55. partMessage = partMessage .. partOfLine
  56. end
  57. elseif msg == "closed" then
  58. --self.conn:shutdown()
  59. print("[NET] Disconnected.")
  60. if self.callbacks.disconnected then
  61. self.callbacks.disconnected( self.kickMsg )
  62. end
  63. self.conn = nil
  64. return false
  65. else
  66. print("[NET] Err Received:", msg, data)
  67. end
  68. end
  69. if not messageLength then
  70. if #partMessage >= 1 then
  71. local headerLength = nil
  72. messageLength, headerLength = utility:headerToLength( partMessage:sub(1,5) )
  73. if messageLength and headerLength then
  74. partMessage = partMessage:sub(headerLength + 1, #partMessage )
  75. end
  76. end
  77. end
  78. -- if I already know how long the message should be:
  79. if messageLength then
  80. if #partMessage >= messageLength then
  81. -- Get actual message:
  82. local currentMsg = partMessage:sub(1, messageLength)
  83. -- Remember rest of already received messages:
  84. partMessage = partMessage:sub( messageLength + 1, #partMessage )
  85. command, content = string.match( currentMsg, "(.)(.*)")
  86. command = string.byte( command )
  87. self:received( command, content )
  88. messageLength = nil
  89. end
  90. end
  91. --[[if data then
  92. if #partMessage > 0 then
  93. data = partMessage .. data
  94. partMessage = ""
  95. end
  96. -- First letter stands for the command:
  97. command, content = string.match(data, "(.)(.*)")
  98. command = string.byte( command )
  99. self:received( command, content )
  100. else
  101. if msg == "timeout" then -- only part of the message could be received
  102. if #partOfLine > 0 then
  103. partMessage = partMessage .. partOfLine
  104. end
  105. elseif msg == "closed" then
  106. --self.conn:shutdown()
  107. print("[NET] Disconnected.")
  108. if self.callbacks.disconnected then
  109. self.callbacks.disconnected( self.kickMsg )
  110. end
  111. self.conn = nil
  112. return false
  113. else
  114. print("[NET] Err Received:", msg, data)
  115. end
  116. end]]
  117. return true
  118. else
  119. return false
  120. end
  121. end
  122. function Client:received( command, msg )
  123. if command == CMD.PING then
  124. -- Respond to ping:
  125. self:send( CMD.PONG, "" )
  126. elseif command == CMD.USER_PINGTIME then
  127. local id, ping = msg:match("(.-)|(.*)")
  128. id = tonumber(id)
  129. if userList[id] then
  130. userList[id].ping.pingReturnTime = tonumber(ping)
  131. end
  132. elseif command == CMD.NEW_PLAYER then
  133. local id, playerName = string.match( msg, "(.*)|(.*)" )
  134. id = tonumber(id)
  135. local user = User:new( nil, playerName, id )
  136. userList[id] = user
  137. numberOfUsers = numberOfUsers + 1
  138. if self.playerName ~= playerName then
  139. if self.callbacks.otherUserConnected then
  140. self.callbacks.otherUserConnected( user )
  141. end
  142. end
  143. elseif command == CMD.PLAYER_LEFT then
  144. local id = tonumber(msg)
  145. local u = userList[id]
  146. userList[id] = nil
  147. numberOfUsers = numberOfUsers - 1
  148. if self.callbacks.otherUserDisconnected then
  149. self.callbacks.otherUserDisconnected( u )
  150. end
  151. elseif command == CMD.AUTHORIZED then
  152. local authed, reason = string.match( msg, "(.*)|(.*)" )
  153. if authed == "true" then
  154. self.authorized = true
  155. print( "[NET] Connection authorized by server." )
  156. -- When authorized, send player name:
  157. self:send( CMD.PLAYERNAME, self.playerName )
  158. else
  159. print( "[NET] Not authorized to join server. Reason: " .. reason )
  160. end
  161. if self.callbacks.authorized then
  162. self.callbacks.authorized( self.authorized, reason )
  163. end
  164. elseif command == CMD.PLAYERNAME then
  165. local id, playerName = string.match( msg, "(.*)|(.*)" )
  166. self.playerName = playerName
  167. self.clientID = tonumber(id)
  168. -- At this point I am fully connected!
  169. if self.callbacks.connected then
  170. self.callbacks.connected()
  171. end
  172. --self.conn:settimeout(5)
  173. elseif command == CMD.USER_VALUE then
  174. local id, keyType, key, valueType, value = string.match( msg, "(.*)|(.*)|(.*)|(.*)|(.*)" )
  175. key = stringToType( key, keyType )
  176. value = stringToType( value, valueType )
  177. id = tonumber( id )
  178. userList[id].customData[key] = value
  179. if self.callbacks.customDataChanged then
  180. self.callback.customDataChanged( user, value, key )
  181. end
  182. elseif command == CMD.KICKED then
  183. self.kickMsg = msg
  184. print("[NET] Kicked from server: " .. msg )
  185. elseif self.callbacks.received then
  186. self.callbacks.received( command, msg )
  187. end
  188. end
  189. function Client:send( command, msg )
  190. local fullMsg = string.char(command) .. (msg or "") --.. "\n"
  191. local len = #fullMsg
  192. assert( len < 256^4, "Length of packet must not be larger than 4GB" )
  193. fullMsg = utility:lengthToHeader( len ) .. fullMsg
  194. local result, err, num = self.conn:send( fullMsg )
  195. while err == "timeout" do
  196. fullMsg = fullMsg:sub( num+1, #fullMsg )
  197. result, err, num = self.conn:send( fullMsg )
  198. end
  199. return
  200. end
  201. function Client:getUsers()
  202. return userList
  203. end
  204. function Client:getNumUsers()
  205. return numberOfUsers
  206. end
  207. function Client:close()
  208. if self.conn then
  209. --self.conn:shutdown()
  210. self.conn:close()
  211. print( "[NET] Closed.")
  212. end
  213. end
  214. function Client:setUserValue( key, value )
  215. local keyType = type( key )
  216. local valueType = type( value )
  217. self:send( CMD.USER_VALUE, keyType .. "|" .. tostring(key) ..
  218. "|" .. valueType .. "|" .. tostring(value) )
  219. end
  220. function Client:getID()
  221. return self.clientID
  222. end
  223. function Client:getUserValue( key )
  224. if not self.clientID then return nil end
  225. local u = userList[self.clientID]
  226. if u then
  227. return u.customData[key]
  228. end
  229. return nil
  230. end
  231. function Client:getUserPing( id )
  232. if users[id] then
  233. return users[id].ping.pingReturnTime
  234. end
  235. end
  236. return Client