server.lua 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. local PlayerServer = extend(app.player.base)
  2. function PlayerServer:activate()
  3. self.shields = {}
  4. self.hurtHistory = {}
  5. self.helpHistory = {}
  6. self.history = {}
  7. self.ack = tick
  8. self.spawnTimer = 0
  9. self.spawnMultiplier = 0
  10. app.player.base.activate(self)
  11. end
  12. function PlayerServer:get(t)
  13. if not self.history or t == tick then return self end
  14. while self.history[1] and self.history[1].tick < tick - 2 / tickRate do
  15. table.remove(self.history, 1)
  16. end
  17. if #self.history == 0 then return self end
  18. if self.history[#self.history].tick < t then return self end
  19. for i = #self.history, 1, -1 do
  20. if self.history[i].tick <= t then return self.history[i] end
  21. end
  22. return self.history[1]
  23. end
  24. function PlayerServer:update()
  25. if self.ded then
  26. self:time()
  27. return app.player.base.update(self)
  28. end
  29. self:time()
  30. self:logic()
  31. if self.health < self.maxHealth and not self.ded then
  32. local percentage = ((tick - self.lastHurt) - (3 / tickRate)) / (10 / tickRate)
  33. if percentage > 0 then
  34. percentage = (1 + (percentage * 7)) / 100
  35. self:heal({amount = self.maxHealth * percentage * tickRate})
  36. end
  37. end
  38. self.spawnTimer = timer.rot(self.spawnTimer)
  39. if self.spawnTimer == 0 then
  40. local spawn = ctx.collision:circleTest(self.x, self.y, self.radius, {tag = 'spawnroom'})
  41. if spawn then
  42. self.spawnMultiplier = self.spawnMultiplier + 1
  43. local amount = 8 * self.spawnMultiplier
  44. if spawn.team == self.team then
  45. self.health = math.min(self.health + amount, self.maxHealth)
  46. else
  47. ctx.event:emit(app.net.events.damage, {id = self.id, amount = amount, from = self.id, tick = tick})
  48. end
  49. else
  50. self.spawnMultiplier = 0
  51. end
  52. self.spawnTimer = .5
  53. end
  54. app.player.base.update(self)
  55. end
  56. function PlayerServer:trace(data, ping)
  57. if data.tick > self.ack then
  58. -- Lag compensation
  59. local oldData = {}
  60. local newData = {}
  61. if ping > 0 then
  62. local target = data.tick - (((ping / 2) / 1000) + interp) / tickRate
  63. local t1 = math.floor(target)
  64. local factor = target - t1
  65. ctx.players:each(function(p)
  66. if p.id ~= self.id then
  67. oldData[p.id] = {p.x, p.y, p.angle}
  68. local s1, s2 = p:get(t1), p:get(t1 + 1)
  69. s1 = {x = s1.x, y = s1.y, angle = s1.angle}
  70. s2 = {x = s2.x, y = s2.y, angle = s2.angle}
  71. local lerpd = table.interpolate(s1, s2, factor)
  72. p.x = lerpd.x
  73. p.y = lerpd.y
  74. newData[p.id] = {p.x, p.y}
  75. p.angle = lerpd.angle
  76. ctx.event:emit('collision.move', {object = p, resolve = true})
  77. end
  78. end)
  79. end
  80. self.ack = data.tick
  81. local prev = self:get(data.tick - 1)
  82. if prev then prev = prev.input end
  83. if not self.ded then
  84. self:move(data)
  85. self:turn(data)
  86. self:slot(data, prev)
  87. end
  88. table.insert(self.history, setmetatable({
  89. x = self.x,
  90. y = self.y,
  91. z = self.z,
  92. angle = self.angle,
  93. input = data,
  94. tick = data.tick
  95. }, self.meta))
  96. -- sync
  97. local msg = {}
  98. msg.x = math.round(self.x * 10)
  99. msg.y = math.round(self.y * 10)
  100. msg.z = math.round(self.z)
  101. msg.angle = math.round((math.deg(self.angle) + 360) % 360)
  102. local shield = 0
  103. table.each(self.shields, function(s) shield = shield + s.health end)
  104. msg.health = math.round(self.health)
  105. msg.shield = math.round(shield)
  106. if data.slot then
  107. msg.weapon = self.weapon
  108. msg.skill = self.skill
  109. end
  110. msg.id = self.id
  111. msg.tick = tick
  112. msg.ack = self.ack
  113. ctx.net:emit(app.net.events.sync, msg)
  114. -- Undo lag compensation
  115. ctx.players:each(function(p)
  116. if oldData[p.id] then
  117. local offsetx, offsety = p.x - newData[p.id][1], p.y - newData[p.id][2]
  118. p.x, p.y, p.angle = unpack(oldData[p.id])
  119. p.x, p.y = p.x + offsetx, p.y + offsety
  120. end
  121. end)
  122. end
  123. end
  124. function PlayerServer:time()
  125. self.ded = timer.rot(self.ded, function() ctx.net:emit(app.net.events.spawn, {id = self.id}) end)
  126. if self.ded == 0 then self.ded = false end
  127. end
  128. function PlayerServer:spell(kind)
  129. app.player.base.spell(self, kind)
  130. end
  131. function PlayerServer:hurt(data)
  132. if self.ded then return end
  133. local target = self.shields[1] or self
  134. while #self.hurtHistory > 0 and self.hurtHistory[1].tick < tick - (10 / tickRate) do
  135. table.remove(self.hurtHistory, 1)
  136. end
  137. table.insert(self.hurtHistory, {tick = data.tick, amount = data.amount, from = data.from})
  138. target.health = math.max(target.health - data.amount, 0)
  139. if target.health <= 0 then
  140. if target == self then
  141. self.ded = 5
  142. local playerHurt = {}
  143. for i = 1, 16 do playerHurt[i] = {i, 0} end
  144. playerHurt[data.from][2] = -1
  145. playerHurt[self.id][2] = -1
  146. for i, v in ipairs(self.hurtHistory) do
  147. if v.from ~= data.from and v.from ~= self.id then
  148. playerHurt[v.from][2] = playerHurt[v.from][2] + v.amount * (1 - ((data.tick - v.tick) / (10 / tickRate)))
  149. end
  150. end
  151. table.sort(playerHurt, function(a, b)
  152. return a[2] > b[2]
  153. end)
  154. local assists = {}
  155. if playerHurt[1][2] > 0 then
  156. table.insert(assists, {id = playerHurt[1][1]})
  157. if playerHurt[2][2] > 0 then table.insert(assists, {id = playerHurt[2][1]}) end
  158. end
  159. ctx.net:emit(app.net.events.dead, {id = self.id, kill = data.from, assists = assists})
  160. else
  161. f.exe(self.shields[1].callback, self)
  162. table.remove(self.shields, 1)
  163. end
  164. end
  165. end
  166. function PlayerServer:heal(data)
  167. self.health = math.min(self.health + data.amount, self.maxHealth)
  168. end
  169. function PlayerServer:spawn()
  170. table.clear(self.hurtHistory)
  171. table.clear(self.helpHistory)
  172. app.player.base.spawn(self)
  173. end
  174. PlayerServer.logic = f.empty
  175. return PlayerServer