termfront 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 361fd9f04e455824ea42245b3dae9c5eda732f7846a8345756809353bebc0b6d
4
- data.tar.gz: 50480fe780ff0cc795004616228aa7042889fbe606289645cb3c973b3657005f
3
+ metadata.gz: 2fd6067c562cc7a0a01f4c71d3e35582366c77f2e9d677a84fcc6bc30a800748
4
+ data.tar.gz: 0773d031711ed21fcac539ae9b9e90a84c058308ed6d4ea5c7393b4dc85248ff
5
5
  SHA512:
6
- metadata.gz: ae66c3672d820736a3155a2d78b41456f90c4f4043421d32b4cbef5b610e4aa697886092e15f64ef086fff5232574bbdae82b977de099df2c9bd00440d1238c8
7
- data.tar.gz: 56ad7d6332ac2828cbd75b0b8087a8230367be8c4462f161c4db731ae4f6203daeaeb77a49d10fd4063f09778b64091406ddd06026e6a9f08ecc3eef397b59eb
6
+ metadata.gz: 94d5836a718e20fc6fce13a642f19d30e36294bed15703e265288179a43b0ae3960613cda90cf91f553175a81f2d6b7e2fe4ac2c73b1856f467fc498464e7fea
7
+ data.tar.gz: 2416935f643801b897a5b9080a5bd6570bf9bfe2e13e310ada37696a6f1d1ce2630af6b8397470ac46fc06ffc58baec8aae43e8ac8233d1dc6f89170abbe2e1f
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 4.0.5
data/CHANGELOG.md CHANGED
@@ -6,6 +6,18 @@ The format is based on Keep a Changelog, and this project follows Semantic Versi
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.1.2] - 2026-05-23
10
+
11
+ ### Added
12
+
13
+ - Added TLS verification coverage for multiplayer connection settings and certificate trust handling
14
+
15
+ ### Changed
16
+
17
+ - Added visible remote shot tracer effects during PvP and Wavesfight multiplayer matches
18
+ - Enabled TLS certificate chain and hostname verification for multiplayer client connections
19
+ - Added support for `TERMFRONT_TLS_CERT_FILE` and `TERMFRONT_TLS_KEY_FILE` when loading the server certificate
20
+
9
21
  ## [0.1.1] - 2026-05-21
10
22
 
11
23
  ### Changed
data/README.md CHANGED
@@ -54,6 +54,14 @@ Start a PvP server:
54
54
  termfront-server
55
55
  ```
56
56
 
57
+ Use custom TLS certificate paths:
58
+
59
+ ```bash
60
+ TERMFRONT_TLS_CERT_FILE=/path/to/fullchain.pem \
61
+ TERMFRONT_TLS_KEY_FILE=/path/to/privkey.pem \
62
+ termfront-server
63
+ ```
64
+
57
65
  Use a custom port:
58
66
 
59
67
  ```bash
@@ -110,7 +118,7 @@ Wavesfight is a PvE survival mode built on selected campaign maps.
110
118
  PvP is currently marked experimental.
111
119
 
112
120
  - The server listens on TCP and wraps traffic with TLS.
113
- - The client connects directly to `host:port`.
121
+ - The client connects directly to `host:port` and verifies the server certificate and hostname.
114
122
  - The default multiplayer endpoint is `termfront.gamelinks007.net:443`.
115
123
  - Matchmaking now supports `1v1`, `2v2`, and `4v4`.
116
124
  - Players choose the match size on the client, and the server keeps separate queues for each mode.
@@ -336,6 +336,7 @@ module Termfront
336
336
  remote[:current].health = msg[:h]
337
337
  remote[:current].weapon = msg[:w]&.to_sym
338
338
  remote[:current].ammo = msg[:am]
339
+ spawn_remote_projectile_effect(msg) if (remote[:current].fire_flash || 0) <= 0 && (msg[:ff] || 0) > 0
339
340
  remote[:current].fire_flash = msg[:ff] || 0
340
341
  remote[:lerp_t] = 0.0
341
342
  end
@@ -377,6 +378,7 @@ module Termfront
377
378
 
378
379
  process_fire_pvp if @player.fire_flash == 4
379
380
  @player.fire_flash -= 1 if @player.fire_flash > 0
381
+ update_projectile_effects(dt)
380
382
 
381
383
  @player.update_shield(dt, @stdout, audio: @audio)
382
384
 
@@ -476,6 +478,7 @@ module Termfront
476
478
  render_pvp_hud(buf, cols)
477
479
  render_view(buf, view_h, view_w, wtop, wbot, wcol)
478
480
  render_remote_players_3d(buf, view_h, view_w, virt_h, dists)
481
+ render_pvp_projectiles(buf, view_h, view_w, virt_h, dists)
479
482
  buf << "\e[#{3 + view_h};1H"
480
483
  render_pvp_radar(buf, cols, radar_h)
481
484
  render_crosshair(buf, view_h, view_w, cols)
@@ -635,6 +638,64 @@ module Termfront
635
638
  end
636
639
  end
637
640
 
641
+ def render_pvp_projectiles(buf, view_h, view_w, virt_h, dists)
642
+ dx = Math.cos(@player.angle)
643
+ dy = Math.sin(@player.angle)
644
+ px = -dy * Math.tan(Config::FOV / 2.0)
645
+ py = dx * Math.tan(Config::FOV / 2.0)
646
+ inv = 1.0 / (px * dy - py * dx)
647
+
648
+ sprites = []
649
+ @projectiles.each do |projectile|
650
+ ex = projectile[:x] - @player.x
651
+ ey = projectile[:y] - @player.y
652
+ tx = inv * (dy * ex - dx * ey)
653
+ tz = inv * (-py * ex + px * ey)
654
+ next if tz < 0.2
655
+
656
+ sprites << [tz, tx, projectile]
657
+ end
658
+ sprites.sort_by! { |sprite| -sprite[0] }
659
+
660
+ sprites.each do |tz, tx, projectile|
661
+ sx = ((view_w / 2.0) * (1 + tx / tz)).to_i
662
+ pw = (4.0 / tz).ceil.clamp(1, 5)
663
+ ph = (virt_h / tz * 0.15).ceil.clamp(2, 6)
664
+ vmid = virt_h / 2
665
+ draw_top = [(vmid - ph / 2), 0].max
666
+ draw_bot = [(vmid + ph / 2).clamp(draw_top + 2, virt_h), virt_h].min
667
+ start_x = [sx - pw / 2, 0].max
668
+ end_x = [sx + pw / 2, view_w - 1].min
669
+
670
+ start_x.upto(end_x) do |c|
671
+ next if c < 0 || c >= view_w
672
+ next if dists[c] < tz
673
+
674
+ r_top = (draw_top / 2.0).ceil
675
+ r_bot = [(draw_bot / 2.0).floor, r_top + 1].max
676
+ r_top.upto(r_bot - 1) do |r|
677
+ next if r < 0 || r >= view_h
678
+
679
+ vp0 = r * 2
680
+ vp1 = r * 2 + 1
681
+ top_in = vp0 >= draw_top && vp0 < draw_bot
682
+ bot_in = vp1 >= draw_top && vp1 < draw_bot
683
+ next unless top_in || bot_in
684
+
685
+ proj_color = projectile[:color]
686
+ buf << "\e[#{3 + r};#{c + 1}H"
687
+ buf << if top_in && bot_in
688
+ "\e[38;2;#{proj_color}m\xE2\x96\x88\e[0m"
689
+ elsif top_in
690
+ "\e[38;2;#{proj_color}m\xE2\x96\x80\e[0m"
691
+ else
692
+ "\e[38;2;#{proj_color}m\xE2\x96\x84\e[0m"
693
+ end
694
+ end
695
+ end
696
+ end
697
+ end
698
+
638
699
  def tint_player_color(color, mode)
639
700
  return nil unless color
640
701
  return color if mode == :ally
@@ -822,6 +883,30 @@ module Termfront
822
883
  STDIN.raw { |s| s.getc }
823
884
  end
824
885
 
886
+ def spawn_remote_projectile_effect(msg)
887
+ shock = msg[:w].to_s.start_with?("shock")
888
+ color = shock ? "80;220;255" : "255;210;80"
889
+ speed = shock ? 18.0 : 14.0
890
+ angle = msg[:a]
891
+ @projectiles << {
892
+ x: msg[:x],
893
+ y: msg[:y],
894
+ vx: Math.cos(angle) * speed,
895
+ vy: Math.sin(angle) * speed,
896
+ ttl: 0.18,
897
+ color: color
898
+ }
899
+ end
900
+
901
+ def update_projectile_effects(dt)
902
+ @projectiles.reject! do |projectile|
903
+ projectile[:x] += projectile[:vx] * dt
904
+ projectile[:y] += projectile[:vy] * dt
905
+ projectile[:ttl] -= dt
906
+ projectile[:ttl] <= 0 || @map.wall_at?(projectile[:x], projectile[:y])
907
+ end
908
+ end
909
+
825
910
  def notify_death_if_needed
826
911
  return unless @player.dead
827
912
  return if @sent_dead
@@ -3,29 +3,47 @@
3
3
  require "socket"
4
4
  require "openssl"
5
5
  require "json"
6
+ require "time"
6
7
 
7
8
  module Termfront
8
9
  module Network
9
10
  class Connection
11
+ PeerInfo = Struct.new(
12
+ :certificate_sha256,
13
+ :public_key_sha256,
14
+ :subject,
15
+ :issuer,
16
+ :not_after,
17
+ keyword_init: true
18
+ )
19
+
10
20
  attr_reader :rtt
21
+ attr_reader :peer_info
11
22
 
12
23
  def initialize
13
24
  @sock = nil
14
25
  @buf = +""
15
26
  @ping_ts = 0
16
27
  @rtt = 0
28
+ @peer_info = nil
17
29
  end
18
30
 
19
- def connect(host, port)
31
+ def connect(host, port, ca_file: nil)
20
32
  tcp = TCPSocket.new(host, port)
21
33
  tcp.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
22
34
 
23
- ctx = OpenSSL::SSL::SSLContext.new
24
- ctx.verify_mode = OpenSSL::SSL::VERIFY_NONE
35
+ ctx = build_ssl_context(ca_file: ca_file)
25
36
  @sock = OpenSSL::SSL::SSLSocket.new(tcp, ctx)
26
37
  @sock.hostname = host if @sock.respond_to?(:hostname=)
27
38
  @sock.sync = true
28
39
  @sock.connect
40
+ @sock.post_connection_check(host)
41
+ @peer_info = build_peer_info(@sock.peer_cert)
42
+ rescue StandardError
43
+ tcp&.close
44
+ @sock = nil
45
+ @peer_info = nil
46
+ raise
29
47
  end
30
48
 
31
49
  def send_msg(hash)
@@ -76,6 +94,7 @@ module Termfront
76
94
  nil
77
95
  end
78
96
  @sock = nil
97
+ @peer_info = nil
79
98
  end
80
99
 
81
100
  def connected?
@@ -96,6 +115,31 @@ module Termfront
96
115
  def clock
97
116
  Process.clock_gettime(Process::CLOCK_MONOTONIC)
98
117
  end
118
+
119
+ def build_ssl_context(ca_file:)
120
+ ctx = OpenSSL::SSL::SSLContext.new
121
+ ctx.verify_mode = OpenSSL::SSL::VERIFY_PEER
122
+ ctx.verify_hostname = true if ctx.respond_to?(:verify_hostname=)
123
+ ctx.cert_store = build_cert_store(ca_file: ca_file)
124
+ ctx
125
+ end
126
+
127
+ def build_cert_store(ca_file:)
128
+ store = OpenSSL::X509::Store.new
129
+ store.set_default_paths
130
+ store.add_file(ca_file) if ca_file
131
+ store
132
+ end
133
+
134
+ def build_peer_info(cert)
135
+ PeerInfo.new(
136
+ certificate_sha256: OpenSSL::Digest::SHA256.hexdigest(cert.to_der),
137
+ public_key_sha256: OpenSSL::Digest::SHA256.hexdigest(cert.public_key.to_der),
138
+ subject: cert.subject.to_s,
139
+ issuer: cert.issuer.to_s,
140
+ not_after: cert.not_after.utc
141
+ )
142
+ end
99
143
  end
100
144
  end
101
145
  end
@@ -589,8 +589,8 @@ module Termfront
589
589
  end
590
590
 
591
591
  def load_or_create_cert
592
- cert_file = "termfront_server.crt"
593
- key_file = "termfront_server.key"
592
+ cert_file = ENV.fetch("TERMFRONT_TLS_CERT_FILE", "termfront_server.crt")
593
+ key_file = ENV.fetch("TERMFRONT_TLS_KEY_FILE", "termfront_server.key")
594
594
 
595
595
  if File.exist?(cert_file) && File.exist?(key_file)
596
596
  cert = OpenSSL::X509::Certificate.new(File.read(cert_file))
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Termfront
4
- VERSION = "0.1.1"
4
+ VERSION = "0.1.2"
5
5
  end
metadata CHANGED
@@ -1,119 +1,119 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: termfront
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
- - S-H-GAMELINKS
7
+ - S-H-GAMELINKS
8
8
  bindir: exe
9
9
  cert_chain: []
10
10
  date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies: []
12
- description: "A terminal-based first-person shooter with DDA raycasting, campaign mode, and PvP multiplayer."
12
+ description: A terminal-based first-person shooter with DDA raycasting, campaign mode,
13
+ and PvP multiplayer.
13
14
  email:
14
- - gamelinks007@gmail.com
15
+ - gamelinks007@gmail.com
15
16
  executables:
16
- - termfront
17
- - termfront-server
17
+ - termfront
18
+ - termfront-server
18
19
  extensions: []
19
20
  extra_rdoc_files: []
20
21
  files:
21
- - CHANGELOG.md
22
- - LICENSE
23
- - LICENSE.txt
24
- - README.md
25
- - Rakefile
26
- - data/audio/THIRD_PARTY_NOTICES.md
27
- - data/audio/beep_02.ogg
28
- - data/audio/button1.ogg
29
- - data/audio/complete.ogg
30
- - data/audio/manifest.json
31
- - data/audio/mission_bgm.wav
32
- - data/audio/mission_clear_se.wav
33
- - data/audio/on.ogg
34
- - data/audio/page_se.wav
35
- - data/audio/sector.mp3
36
- - data/audio/sfx_22b.ogg
37
- - data/audio/shield_alarm_se.wav
38
- - data/audio/shield_regen_se.wav
39
- - data/audio/shoot_01.ogg
40
- - data/audio/terminal_se.wav
41
- - data/audio/title.mp3
42
- - data/audio/title_bgm.wav
43
- - data/audio/victory.mp3
44
- - data/events/corridor_sweep.json
45
- - data/events/final_push.json
46
- - data/events/stronghold.json
47
- - data/events/the_gauntlet.json
48
- - data/events/training_grounds.json
49
- - exe/termfront
50
- - exe/termfront-server
51
- - lib/termfront.rb
52
- - lib/termfront/audio_manager.rb
53
- - lib/termfront/config.rb
54
- - lib/termfront/demo_player.rb
55
- - lib/termfront/drop_item/base.rb
56
- - lib/termfront/drop_item/weapon.rb
57
- - lib/termfront/enemy/base.rb
58
- - lib/termfront/enemy/crawler.rb
59
- - lib/termfront/enemy/executor.rb
60
- - lib/termfront/game.rb
61
- - lib/termfront/input.rb
62
- - lib/termfront/map.rb
63
- - lib/termfront/mission/base.rb
64
- - lib/termfront/mission/corridor_sweep.rb
65
- - lib/termfront/mission/event_loader.rb
66
- - lib/termfront/mission/event_runtime.rb
67
- - lib/termfront/mission/final_push.rb
68
- - lib/termfront/mission/stronghold.rb
69
- - lib/termfront/mission/the_gauntlet.rb
70
- - lib/termfront/mission/training.rb
71
- - lib/termfront/mission/training_grounds.rb
72
- - lib/termfront/network/client.rb
73
- - lib/termfront/network/connection.rb
74
- - lib/termfront/network/server.rb
75
- - lib/termfront/network/wavesfight_client.rb
76
- - lib/termfront/opponent.rb
77
- - lib/termfront/player.rb
78
- - lib/termfront/projectile.rb
79
- - lib/termfront/remote_enemy.rb
80
- - lib/termfront/renderer.rb
81
- - lib/termfront/scene_player.rb
82
- - lib/termfront/sprite.rb
83
- - lib/termfront/terminal_output.rb
84
- - lib/termfront/title_screen.rb
85
- - lib/termfront/version.rb
86
- - lib/termfront/weapon/assault_rifle.rb
87
- - lib/termfront/weapon/base.rb
88
- - lib/termfront/weapon/pistol.rb
89
- - lib/termfront/weapon/shock_pistol.rb
90
- - lib/termfront/weapon/shock_rifle.rb
91
- - sig/termfront.rbs
92
- homepage: "https://github.com/S-H-GAMELINKS/termfront"
22
+ - ".ruby-version"
23
+ - CHANGELOG.md
24
+ - LICENSE
25
+ - LICENSE.txt
26
+ - README.md
27
+ - Rakefile
28
+ - data/audio/THIRD_PARTY_NOTICES.md
29
+ - data/audio/beep_02.ogg
30
+ - data/audio/button1.ogg
31
+ - data/audio/complete.ogg
32
+ - data/audio/manifest.json
33
+ - data/audio/mission_bgm.wav
34
+ - data/audio/mission_clear_se.wav
35
+ - data/audio/on.ogg
36
+ - data/audio/page_se.wav
37
+ - data/audio/sector.mp3
38
+ - data/audio/sfx_22b.ogg
39
+ - data/audio/shield_alarm_se.wav
40
+ - data/audio/shield_regen_se.wav
41
+ - data/audio/shoot_01.ogg
42
+ - data/audio/terminal_se.wav
43
+ - data/audio/title.mp3
44
+ - data/audio/title_bgm.wav
45
+ - data/audio/victory.mp3
46
+ - data/events/corridor_sweep.json
47
+ - data/events/final_push.json
48
+ - data/events/stronghold.json
49
+ - data/events/the_gauntlet.json
50
+ - data/events/training_grounds.json
51
+ - exe/termfront
52
+ - exe/termfront-server
53
+ - lib/termfront.rb
54
+ - lib/termfront/audio_manager.rb
55
+ - lib/termfront/config.rb
56
+ - lib/termfront/demo_player.rb
57
+ - lib/termfront/drop_item/base.rb
58
+ - lib/termfront/drop_item/weapon.rb
59
+ - lib/termfront/enemy/base.rb
60
+ - lib/termfront/enemy/crawler.rb
61
+ - lib/termfront/enemy/executor.rb
62
+ - lib/termfront/game.rb
63
+ - lib/termfront/input.rb
64
+ - lib/termfront/map.rb
65
+ - lib/termfront/mission/base.rb
66
+ - lib/termfront/mission/corridor_sweep.rb
67
+ - lib/termfront/mission/event_loader.rb
68
+ - lib/termfront/mission/event_runtime.rb
69
+ - lib/termfront/mission/final_push.rb
70
+ - lib/termfront/mission/stronghold.rb
71
+ - lib/termfront/mission/the_gauntlet.rb
72
+ - lib/termfront/mission/training.rb
73
+ - lib/termfront/mission/training_grounds.rb
74
+ - lib/termfront/network/client.rb
75
+ - lib/termfront/network/connection.rb
76
+ - lib/termfront/network/server.rb
77
+ - lib/termfront/network/wavesfight_client.rb
78
+ - lib/termfront/opponent.rb
79
+ - lib/termfront/player.rb
80
+ - lib/termfront/projectile.rb
81
+ - lib/termfront/remote_enemy.rb
82
+ - lib/termfront/renderer.rb
83
+ - lib/termfront/scene_player.rb
84
+ - lib/termfront/sprite.rb
85
+ - lib/termfront/terminal_output.rb
86
+ - lib/termfront/title_screen.rb
87
+ - lib/termfront/version.rb
88
+ - lib/termfront/weapon/assault_rifle.rb
89
+ - lib/termfront/weapon/base.rb
90
+ - lib/termfront/weapon/pistol.rb
91
+ - lib/termfront/weapon/shock_pistol.rb
92
+ - lib/termfront/weapon/shock_rifle.rb
93
+ - sig/termfront.rbs
94
+ homepage: https://github.com/S-H-GAMELINKS/termfront
93
95
  licenses:
94
- - MIT
96
+ - MIT
95
97
  metadata:
96
- allowed_push_host: "https://rubygems.org"
97
- homepage_uri: "https://github.com/S-H-GAMELINKS/termfront"
98
- source_code_uri: "https://github.com/S-H-GAMELINKS/termfront"
99
- changelog_uri: "https://github.com/S-H-GAMELINKS/termfront/blob/main/CHANGELOG.md"
98
+ allowed_push_host: https://rubygems.org
99
+ homepage_uri: https://github.com/S-H-GAMELINKS/termfront
100
+ source_code_uri: https://github.com/S-H-GAMELINKS/termfront
101
+ changelog_uri: https://github.com/S-H-GAMELINKS/termfront/blob/main/CHANGELOG.md
100
102
  rdoc_options: []
101
103
  require_paths:
102
- - lib
104
+ - lib
103
105
  required_ruby_version: !ruby/object:Gem::Requirement
104
106
  requirements:
105
- -
106
- - ">="
107
- - !ruby/object:Gem::Version
108
- version: 3.2.0
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ version: 3.2.0
109
110
  required_rubygems_version: !ruby/object:Gem::Requirement
110
111
  requirements:
111
- -
112
- - ">="
113
- - !ruby/object:Gem::Version
114
- version: "0"
112
+ - - ">="
113
+ - !ruby/object:Gem::Version
114
+ version: '0'
115
115
  requirements: []
116
- rubygems_version: 4.1.0.dev
116
+ rubygems_version: 4.0.10
117
117
  specification_version: 4
118
118
  summary: Terminal FPS game with raycasting engine
119
119
  test_files: []