From 9aa5dc7174adda4bf26c38aeb69d28199d7434fb Mon Sep 17 00:00:00 2001 From: cyberarm Date: Wed, 2 Dec 2020 11:38:10 -0600 Subject: [PATCH] Cleanup, moved Map lifecycle into Director, added renderer_info command --- lib/event_handlers/entity_lifecycle.rb | 3 +- lib/game_objects/entities/player.rb | 6 +- lib/managers/entity_manager.rb | 17 ++- lib/map.rb | 98 ++++++++++++++--- lib/networking/backend/connection.rb | 3 +- lib/networking/backend/packet_handler.rb | 6 +- lib/networking/backend/server.rb | 39 +++---- lib/networking/director.rb | 43 +++++--- lib/networking/peer.rb | 23 ---- lib/networking/server.rb | 131 +---------------------- lib/scripting.rb | 2 +- lib/states/game_states/game.rb | 37 ++----- lib/ui/commands/renderer_info_command.rb | 24 +++++ lib/window.rb | 4 +- new_server_test.rb | 4 +- 15 files changed, 188 insertions(+), 252 deletions(-) delete mode 100644 lib/networking/peer.rb create mode 100644 lib/ui/commands/renderer_info_command.rb diff --git a/lib/event_handlers/entity_lifecycle.rb b/lib/event_handlers/entity_lifecycle.rb index 0346696..9b9514d 100644 --- a/lib/event_handlers/entity_lifecycle.rb +++ b/lib/event_handlers/entity_lifecycle.rb @@ -7,10 +7,11 @@ class IMICFPS def handle(subscriber, context, *args) return unless subscriber.entity == args.first.first + event = EventHandler::Event.new(entity: subscriber.entity, context: context) subscriber.trigger(event) end end end -end \ No newline at end of file +end diff --git a/lib/game_objects/entities/player.rb b/lib/game_objects/entities/player.rb index 9dedf3a..e67cba3 100644 --- a/lib/game_objects/entities/player.rb +++ b/lib/game_objects/entities/player.rb @@ -60,9 +60,9 @@ class IMICFPS end def jump - if InputMapper.down?(:jump) && window.current_state.map.collision_manager.on_ground?(self) - @velocity.y = 1.5 - end + return unless InputMapper.down?(:jump) && window.director.map.collision_manager.on_ground?(self) + + @velocity.y = 1.5 end end end diff --git a/lib/managers/entity_manager.rb b/lib/managers/entity_manager.rb index e44e3d2..829d904 100644 --- a/lib/managers/entity_manager.rb +++ b/lib/managers/entity_manager.rb @@ -7,25 +7,24 @@ class IMICFPS end def insert_entity(package, name, position, orientation, data = {}) - ent = MapParser::Entity.new(package, name, position, orientation, Vector.new(1,1,1)) + ent = MapParser::Entity.new(package, name, position, orientation, Vector.new(1, 1, 1)) add_entity(IMICFPS::Entity.new(map_entity: ent, manifest: Manifest.new(package: package, name: name))) end def find_entity(entity) - @entities.detect {|entity| entity == entity} + @entities.detect { |e| e == entity } end def find_entity_by(name:) - @entities.detect { |entity| entity.name == name} + @entities.detect { |entity| entity.name == name } end def remove_entity(entity) - ent = @entities.detect {|entity| entity == entity} - if ent - @collision_manager.remove(entity) if @collision_manager && entity.manifest.collision - @publisher.publish(:destroy, nil, entity) - @entities.delete(ent) - end + return unless (ent = @entities.detect { |e| e == entity }) + + @collision_manager.remove(entity) if @collision_manager && entity.manifest.collision + @publisher.publish(:destroy, nil, entity) + @entities.delete(ent) end def entities diff --git a/lib/map.rb b/lib/map.rb index a94d495..7b733b8 100644 --- a/lib/map.rb +++ b/lib/map.rb @@ -4,8 +4,8 @@ class IMICFPS include LightManager include CommonMethods - attr_reader :collision_manager - attr_reader :gravity + attr_reader :collision_manager, :gravity + def initialize(map_parser:, gravity: IMICFPS::EARTH_GRAVITY) @map_parser = map_parser @gravity = gravity @@ -18,24 +18,92 @@ class IMICFPS end def setup - add_entity(Terrain.new(map_entity: @map_parser.terrain, manifest: Manifest.new(package: @map_parser.terrain.package, name: @map_parser.terrain.name))) + add_terrain + add_skybox + add_lights + add_entities - add_entity(Skydome.new(map_entity: @map_parser.skydome, manifest: Manifest.new(package: @map_parser.skydome.package, name: @map_parser.skydome.name), backface_culling: false)) + # TODO: Add player entity from director + add_entity( + Player.new( + spawnpoint: @map_parser.spawnpoints.sample, + manifest: Manifest.new( + package: "base", + name: "character" + ) + ) + ) + end + def add_terrain + add_entity( + Terrain.new( + map_entity: @map_parser.terrain, + manifest: Manifest.new( + package: @map_parser.terrain.package, + name: @map_parser.terrain.name + ) + ) + ) + end + + def add_skybox + add_entity( + Skydome.new( + map_entity: @map_parser.skydome, + backface_culling: false, + manifest: Manifest.new( + package: @map_parser.skydome.package, + name: @map_parser.skydome.name + ) + ) + ) + end + + def add_lights @map_parser.lights.each do |l| - add_light(Light.new(id: available_light, type: l.type, position: l.position, diffuse: l.diffuse, ambient: l.ambient, specular: l.specular, intensity: l.intensity)) + add_light( + Light.new( + id: available_light, + type: l.type, + position: l.position, + diffuse: l.diffuse, + ambient: l.ambient, + specular: l.specular, + intensity: l.intensity + ) + ) end - @map_parser.entities.each do |ent| - add_entity(Entity.new(map_entity: ent, manifest: Manifest.new(package: ent.package, name: ent.name))) - end - - add_entity(Player.new(spawnpoint: @map_parser.spawnpoints.sample, manifest: Manifest.new(package: "base", name: "character"))) - # Default lights if non are defined - if @map_parser.lights.size == 0 - add_light(Light.new(id: available_light, position: Vector.new(30, 10.0, 30))) - add_light(Light.new(id: available_light, position: Vector.new(0, 100, 0), diffuse: Color.new(1.0, 0.5, 0.1))) + return unless @map_parser.lights.size.zero? + + add_light( + Light.new( + id: available_light, + position: Vector.new(30, 10.0, 30) + ) + ) + + add_light( + Light.new( + id: available_light, + position: Vector.new(0, 100, 0), diffuse: Color.new(1.0, 0.5, 0.1) + ) + ) + end + + def add_entities + @map_parser.entities.each do |ent| + add_entity( + Entity.new( + map_entity: ent, + manifest: Manifest.new( + package: ent.package, + name: ent.name + ) + ) + ) end end @@ -48,7 +116,7 @@ class IMICFPS Gosu.gl do gl_error? - glClearColor(0,0.2,0.5,1) # skyish blue + glClearColor(0, 0.2, 0.5, 1) # skyish blue gl_error? glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) # clear the screen and the depth buffer gl_error? diff --git a/lib/networking/backend/connection.rb b/lib/networking/backend/connection.rb index 31c97b2..f874e9b 100644 --- a/lib/networking/backend/connection.rb +++ b/lib/networking/backend/connection.rb @@ -2,6 +2,7 @@ module CyberarmEngine module Networking class Connection attr_reader :hostname, :port, :peer + def initialize(hostname:, port:, channels: 3) @hostname = hostname @port = port @@ -26,7 +27,7 @@ module CyberarmEngine # Functions # def send_packet(message:, reliable: false, channel: 0) - @peer.write_queue << PacketHandler.create_raw_packet(peer: @peer, message: message, reliable: reliable, channel: channel) + @peer.write_queue << PacketHandler.create_raw_packet(peer: @peer, message: message, reliable: reliable, channel: channel) end def connect(timeout: Protocol::TIMEOUT_PERIOD) diff --git a/lib/networking/backend/packet_handler.rb b/lib/networking/backend/packet_handler.rb index efa92b2..d70c883 100644 --- a/lib/networking/backend/packet_handler.rb +++ b/lib/networking/backend/packet_handler.rb @@ -32,9 +32,9 @@ module CyberarmEngine when Protocol::CONTROL_CONNECT # TOSERVER only if (peer_id = host.available_peer_id) peer.id = peer_id - host.clients << peer + host.peers << peer peer.write_queue << create_control_packet(peer: peer, control_type: Protocol::CONTROL_SET_PEER_ID, message: [peer_id].pack("n")) - host.client_connected(peer: peer) + host.peer_connected(peer: peer) else host.write( peer: peer, @@ -52,7 +52,7 @@ module CyberarmEngine when Protocol::CONTROL_DISCONNECT if host.is_a?(Server) - host.client_disconnected(peer: peer) + host.peer_disconnected(peer: peer) else host.disconnected(reason: pkt.message) end diff --git a/lib/networking/backend/server.rb b/lib/networking/backend/server.rb index e6d2c50..7a1e27d 100644 --- a/lib/networking/backend/server.rb +++ b/lib/networking/backend/server.rb @@ -1,7 +1,7 @@ module CyberarmEngine module Networking class Server - attr_reader :hostname, :port, :max_clients + attr_reader :hostname, :port, :max_peers, :peers attr_accessor :total_packets_sent, :total_packets_received, :total_data_sent, :total_data_received, :last_read_time, :last_write_time @@ -9,12 +9,12 @@ module CyberarmEngine def initialize( hostname: CyberarmEngine::Networking::DEFAULT_SERVER_HOSTNAME, port: CyberarmEngine::Networking::DEFAULT_SERVER_PORT, - max_clients: CyberarmEngine::Networking::DEFAULT_PEER_LIMIT, + max_peers: CyberarmEngine::Networking::DEFAULT_PEER_LIMIT, channels: 3 ) @hostname = hostname @port = port - @max_clients = max_clients + 2 + @max_peers = max_peers + 2 @channels = Array(0..channels).map { |id| Channel.new(id: id, mode: :default) } @peers = [] @@ -28,35 +28,26 @@ module CyberarmEngine @total_data_received = 0 end - # Helpers # - def connected_clients - @peers.size - end - - def clients - @peers - end - # Callbacks # - # Called when client connects - def client_connected(peer:) + # Called when peer connects + def peer_connected(peer:) end - # Called when client times out or explicitly disconnects - def client_disconnected(peer:, reason:) + # Called when peer times out or explicitly disconnects + def peer_disconnected(peer:, reason:) end ### REMOVE? ### - # Called when client was not sending heartbeats or regular packets for a + # Called when peer was not sending heartbeats or regular packets for a # period of time, but was not logically disconnected and removed, and started # send packets again. # - # TLDR: Client was temporarily unreachable but did not timeout. - def client_reconnected(peer:) + # TLDR: peer was temporarily unreachable but did not timeout. + def peer_reconnected(peer:) end - # Called when a (logical) packet is received from client + # Called when a (logical) packet is received from peer def packet_received(peer:, message:, channel:) end @@ -84,7 +75,7 @@ module CyberarmEngine end # Disconnect peer - def disconnect_client(peer:, reason: "") + def disconnect_peer(peer:, reason: "") if (peer = @peers[peer]) packet = PacketHandler.create_disconnect_packet(peer.id, reason) peer.write_now!(packet) @@ -110,7 +101,7 @@ module CyberarmEngine message: message ) ) - client_disconnected(peer: peer, reason: message) + peer_disconnected(peer: peer, reason: message) @peers.delete(peer) next end @@ -135,7 +126,7 @@ module CyberarmEngine def available_peer_id peer_ids = @peers.map(&:id) - ids = (2..@max_clients).to_a - peer_ids + ids = (2..@max_peers).to_a - peer_ids ids.size.positive? ? ids.first : nil end @@ -157,7 +148,7 @@ module CyberarmEngine packet: PacketHandler.create_control_packet( peer: peer, control_type: Protocol::CONTROL_DISCONNECT, - message: "ERROR: client not connected" + message: "ERROR: peer not connected" ) ) end diff --git a/lib/networking/director.rb b/lib/networking/director.rb index eaf25db..3e5e204 100644 --- a/lib/networking/director.rb +++ b/lib/networking/director.rb @@ -1,29 +1,36 @@ class IMICFPS module Networking class Director - attr_reader :address, :port, :tick_rate, :storage, :map - def initialize(address: DEFAULT_SERVER_HOST, port: DEFAULT_SERVER_PORT, tick_rate: 2) - @address = address - @port = port + attr_reader :tick_rate, :storage, :map, :server, :connection + + def initialize(tick_rate: 15) @tick_rate = (1000.0 / tick_rate) / 1000.0 - @server = Server.new(address: @address, port: @port, max_peers: DEFAULT_PEER_LIMIT) - @server.bind - - @last_tick_time = Networking.milliseconds + @last_tick_time = CyberarmEngine::Networking.milliseconds @directing = true @storage = {} @map = nil end + def host_server(hostname: CyberarmEngine::Networking::DEFAULT_SERVER_HOSTNAME, port: CyberarmEngine::Networking::DEFAULT_SERVER_PORT, max_peers: CyberarmEngine::Networking::DEFAULT_PEER_LIMIT) + @server = Server.new(hostname: hostname, port: port, max_peers: max_peers) + @server.bind + end + + def connect(hostname:, port: CyberarmEngine::Networking::DEFAULT_SERVER_PORT) + @connection = Connection.new(hostname: hostname, port: port) + @connection.connect + end + def load_map(map_parser:) # TODO: send map_change to clients @map = Map.new(map_parser: map_parser) + @map.setup end def run - Thread.start do |thread| - while(@directing) + Thread.start do + while @directing dt = milliseconds - @last_tick_time tick(dt) @@ -35,18 +42,20 @@ class IMICFPS end def tick(delta_time) - if @map - Publisher.instance.publish(:tick, delta_time * 1000.0) + return unless @map - @map.update - @server.update - end + Publisher.instance.publish(:tick, delta_time * 1000.0) + + @map.update + @server&.update + @connection&.update end def shutdown @directing = false - @server.close + @server&.close + @connection&.close end end end -end \ No newline at end of file +end diff --git a/lib/networking/peer.rb b/lib/networking/peer.rb deleted file mode 100644 index a27cf16..0000000 --- a/lib/networking/peer.rb +++ /dev/null @@ -1,23 +0,0 @@ -class IMICFPS - module Networking - class Peer - attr_reader :id, :address, :port, :packet_read_queue, :packet_write_queue - attr_accessor :total_packets_sent, :total_packets_received, :total_data_sent, :total_data_received, :last_read_time, :last_write_time - def initialize(id:, address:, port:) - @id = id - @address, @port = address, port - - @packet_write_queue = [] - @packet_read_queue = [] - - @last_read_time = Networking.milliseconds - @last_write_time = Networking.milliseconds - - @total_packets_sent = 0 - @total_packets_received = 0 - @total_data_sent = 0 - @total_data_received = 0 - end - end - end -end \ No newline at end of file diff --git a/lib/networking/server.rb b/lib/networking/server.rb index cf5c7b3..93e3885 100644 --- a/lib/networking/server.rb +++ b/lib/networking/server.rb @@ -1,135 +1,14 @@ class IMICFPS module Networking - class Server - attr_reader :address, :port, :max_peers, :peers - attr_accessor :total_packets_sent, :total_packets_received, :total_data_sent, :total_data_received, :last_read_time, :last_write_time - def initialize(address:, port:, max_peers:) - @address = address - @port = port - @max_peers = max_peers - - @peers = Array.new(@max_peers + 1, nil) - - @read_buffer = ReadBuffer.new - - @last_read_time = Networking.milliseconds - @last_write_time = Networking.milliseconds - - @total_packets_sent = 0 - @total_packets_received = 0 - @total_data_sent = 0 - @total_data_received = 0 + class Server < CyberarmEngine::Networking::Server + def connected(peer:) end - # Peer ID 0 is reserved for unconnected peers to pass packet validation - def create_peer(address:, port:) - @peers.each_with_index do |peer, i| - unless peer - new_peer = Peer.new(id: i + 1, address: address, port: port) - @peers[i + 1] = new_peer - - return new_peer - end - end - - return nil + def disconnected(peer:, reason:) end - def get_peer(peer_id) - @peers[peer_id] - end - - def remove_peer(peer_id) - @peers[peer_id] = nil - end - - def bind - @socket = UDPSocket.new - @socket.bind(@address, @port) - end - - def send_packet( peer_id, packet ) - if peer = get_peer(peer_id) - packets = Packet.splinter(packet) - - packets.each { |pkt| peer.packet_write_queue.push(pkt) } - end - end - - def broadcast_packet(packet) - @peers.each do |peer| - next unless peer - - send_packet(peer.id, packet) - end - end - - def update - while(read) - end - - @peers.each do |peer| - next unless peer - - peer.packet_write_queue.each do |packet| - write(peer, packet) - peer.packet_write_queue.delete(packet) - end - end - - # "deliver" packets to peers, record stats to peers - @read_buffer.reconstruct_packets.each do |packet, addr_info| - peer = nil - - # initial connection - if packet.peer_id == 0 && packet.type == Protocol::CONNECT - peer = create_peer(address: addr_info[2], port: addr_info[1]) - send_packet( - peer.id, - Packet.new( - peer_id: 0, - sequence: 0, - type: Protocol::VERIFY_CONNECT, - payload: [peer.id].pack("C") - ) - ) - else - peer = get_peer(packet.peer_id) - end - end - - # broadcast_packet(Packet.new(peer_id: 0, sequence: 0, type: Protocol::HEARTBEAT, payload: [Networking.milliseconds].pack("G"))) - end - - def close - @socket.close if @socket - end - - private - def read - data, addr = @socket.recvfrom_nonblock(Protocol::MAX_PACKET_SIZE) - @read_buffer.add(data, addr ) - - @total_packets_received += 1 - @total_data_received += data.length - @last_read_time = Networking.milliseconds - return true - rescue IO::WaitReadable - return false - end - - def write(peer, packet) - raw = packet.encode - @socket.send( raw, 0, peer.address, peer.port ) - - @total_packets_sent += 1 - @total_data_sent += raw.length - @last_write_time = Networking.milliseconds - - peer.total_packets_sent += 1 - peer.total_data_sent += raw.length - peer.last_write_time = Networking.milliseconds + def packet_received(peer:, message:, channel:) end end end -end \ No newline at end of file +end diff --git a/lib/scripting.rb b/lib/scripting.rb index fb286d3..2696c08 100644 --- a/lib/scripting.rb +++ b/lib/scripting.rb @@ -10,7 +10,7 @@ class IMICFPS end def map - $window.current_state.map + $window.director.map end end end \ No newline at end of file diff --git a/lib/states/game_states/game.rb b/lib/states/game_states/game.rb index f226c9e..11f3ef2 100644 --- a/lib/states/game_states/game.rb +++ b/lib/states/game_states/game.rb @@ -1,16 +1,13 @@ class IMICFPS class Game < GameState - attr_reader :map - def setup - @map = Map.new(map_parser: @options[:map_parser]) - @map.setup - @player = @map.find_entity_by(name: "character") + def setup + window.director.load_map(map_parser: @options[:map_parser]) + + @player = window.director.map.find_entity_by(name: "character") @camera = PerspectiveCamera.new( position: @player.position.clone, aspect_ratio: window.aspect_ratio ) @camera_controller = CameraController.new(mode: :fpv, camera: @camera, entity: @player) - # @director = Networking::Director.new - # @director.load_map(map_parser: @options[:map_parser]) # @connection = Networking::Connection.new(address: "localhost", port: Networking::DEFAULT_SERVER_PORT) # @connection.connect @@ -26,32 +23,20 @@ class IMICFPS end def draw - @map.render(@camera) + window.director.map.render(@camera) @hud.draw end def update - update_text - control_player @hud.update @camera_controller.update # @connection.update - # @director.tick(window.dt) - @map.update + window.director.tick(window.dt) - @demo.update if @demo - end - - def update_text - string = <<-eos -OpenGL Vendor: #{glGetString(GL_VENDOR)} -OpenGL Renderer: #{glGetString(GL_RENDERER)} -OpenGL Version: #{glGetString(GL_VERSION)} -OpenGL Shader Language Version: #{glGetString(GL_SHADING_LANGUAGE_VERSION)} -eos + @demo&.update end def control_player @@ -73,25 +58,25 @@ eos return end - @demo.button_down(id) if @demo + @demo&.button_down(id) @camera_controller.button_down(id) InputMapper.keydown(id) Publisher.instance.publish(:button_down, nil, id) - @map.entities.each do |entity| + window.director.map.entities.each do |entity| entity.button_down(id) if defined?(entity.button_down) end end def button_up(id) - @demo.button_up(id) if @demo + @demo&.button_up(id) @camera_controller.button_up(id) InputMapper.keyup(id) Publisher.instance.publish(:button_up, nil, id) - @map.entities.each do |entity| + window.director.map.entities.each do |entity| entity.button_up(id) if defined?(entity.button_up) end end diff --git a/lib/ui/commands/renderer_info_command.rb b/lib/ui/commands/renderer_info_command.rb new file mode 100644 index 0000000..ee11588 --- /dev/null +++ b/lib/ui/commands/renderer_info_command.rb @@ -0,0 +1,24 @@ +class IMICFPS + class Commands + class RendererInfoCommand < Command + def group + :global + end + + def command + :renderer_info + end + + def handle(arguments, console) + console.stdin("OpenGL Vendor: #{Style.notice(glGetString(GL_VENDOR))}") + console.stdin("OpenGL Renderer: #{Style.notice(glGetString(GL_RENDERER))}") + console.stdin("OpenGL Version: #{Style.notice(glGetString(GL_VERSION))}") + console.stdin("OpenGL Shader Language Version: #{Style.notice(glGetString(GL_SHADING_LANGUAGE_VERSION))}") + end + + def usage + "#{Style.highlight("renderer_info")} #{Style.notice("Returns OpenGL renderer information")}" + end + end + end +end diff --git a/lib/window.rb b/lib/window.rb index 2dec4b6..1c176d1 100644 --- a/lib/window.rb +++ b/lib/window.rb @@ -1,7 +1,7 @@ class IMICFPS class Window < CyberarmEngine::Window attr_accessor :number_of_vertices, :needs_cursor - attr_reader :renderer, :scene, :config + attr_reader :renderer, :scene, :config, :director attr_reader :console, :delta_time def initialize(window_width = 1280, window_height = 720, fullscreen = false) @@ -23,6 +23,8 @@ class IMICFPS self.caption = "#{IMICFPS::NAME} v#{IMICFPS::VERSION} (#{IMICFPS::RELEASE_NAME})" + @director = Networking::Director.new + @config = CyberarmEngine::ConfigFile.new(file: IMICFPS::GAME_ROOT_PATH + "/data/config.json") @show_console = false @console = Console.new diff --git a/new_server_test.rb b/new_server_test.rb index 4c24835..78548dd 100644 --- a/new_server_test.rb +++ b/new_server_test.rb @@ -29,8 +29,8 @@ require_all "lib/networking/backend" Thread.abort_on_exception = true server = CyberarmEngine::Networking::Server.new -def server.client_connected(peer:) - puts "Client connected as peer: #{peer.id}" +def server.peer_connected(peer:) + puts "Peer connected: #{peer.id}" end def server.packet_received(peer:, message:, channel:)