# frozen_string_literal: true require 'json' require_relative './world.rb' require_relative './serialiser.rb' # This class handles sending/receiving network data + the "game loop" timing. class App FRAMES_PER_SECOND = 60 def initialize @connections = {} @messages = [] @world = World.new @mutex = Mutex.new Thread.new { main_loop } end def connect(cid, &socket) @mutex.synchronize do @connections[cid] = { socket: socket } @messages.append({ from: cid, type: :join }) end end def disconnect(cid) @mutex.synchronize do @connections.delete(cid) @messages.append({ from: cid, type: :quit }) end end def receive(cid, text) json = JSON.parse(text) @mutex.synchronize { @messages.append({ from: cid, data: json }) } rescue JSON::ParserError => e puts("ERROR: #{e}") end private def main_loop timer = milliseconds loop do updated = false while timer <= milliseconds updated = update_world_state timer += 1000.0 / FRAMES_PER_SECOND end update_frame_count if updated sleep(0.001) # Avoid constantly using 100% CPU. end end def update_world_state @mutex.synchronize do @world.update(@messages) @messages = [] end true end def update_frame_count @frame ||= 0 @frame = (@frame + 1) % 120 update_clients(full: @frame.zero?) if (@frame % 6).zero? end def update_clients(full: false) @mutex.synchronize do @connections.each do |cid, data| data[:serialiser] ||= Serialiser.new(cid) state = data[:serialiser].snapshot(@world, full: full) data[:socket].call(state) unless state.nil? # nil = Nothing to send. end end end def milliseconds (Process.clock_gettime(Process::CLOCK_MONOTONIC) * 1000).to_i end end