http forwarder complexity, a lot of the legwork for GSI, presently only supporting fills on csgo, no flashes yet

8bd7ebf · gmorell · 2023-02-05 04:24

21 files +542 -35

Files changed

modified config/prod.exs
+3 −3
@@ -10,9 +10,9 @@ import Config
10 10 # which you should run after static files are built and
11 11 # before starting your production server.
12 12 config :lambent_ex, LambentExWeb.Endpoint,
13 − cache_static_manifest: "priv/static/cache_manifest.json",
14 − check_origin: :conn
15 −
13 + cache_static_manifest: "priv/static/cache_manifest.json"
14 +# check_origin: :conn
15 +#
16 16 # Do not print debug messages in production
17 17 config :logger, level: :info
18 18
modified config/runtime.exs
+16 −1
@@ -17,7 +17,9 @@ import Config
17 17 # Alternatively, you can use `mix phx.gen.release` to generate a `bin/server`
18 18 # script that automatically sets the env var above.
19 19 if System.get_env("PHX_SERVER") do
20 − config :lambent_ex, LambentExWeb.Endpoint, server: true
20 + config :lambent_ex, LambentExWeb.Endpoint,
21 + server: true,
22 + cache_static_manifest: "priv/static/cache_manifest.json"
21 23 end
22 24
23 25 if config_env() == :prod do
@@ -53,6 +55,19 @@ if config_env() == :prod do
53 55 ],
54 56 secret_key_base: secret_key_base
55 57
58 + case host do
59 + "example.com" ->
60 + config :lambent_ex, LambentExWeb.Endpoint, check_origin: :conn
61 +
62 + host ->
63 + config :lambent_ex, LambentExWeb.Endpoint,
64 + http: [port: 4000],
65 + check_origin: false,
66 + server: true,
67 + root: ".",
68 + cache_static_manifest: "priv/static/cache_manifest.json"
69 + end
70 +
56 71 # ## SSL Support
57 72 #
58 73 # To get SSL working, you will need to add the `https` key
modified lib/lambent_ex/machine.ex
+163 −13
@@ -38,7 +38,8 @@ defmodule LambentEx.Machine do
38 38 255 => :FULL
39 39 }
40 40
41 − def start_link(_foo, opts) do # check to see if machine-opts[:step]-name is better in machine-name
41 + # check to see if machine-opts[:step]-name is better in machine-name
42 + def start_link(_foo, opts) do
42 43 Parent.GenServer.start_link(__MODULE__, opts, name: via_tuple("machine-#{opts[:name]}"))
43 44 end
44 45
@@ -47,13 +48,21 @@ defmodule LambentEx.Machine do
47 48 Process.send_after(self(), :publish, 50)
48 49
49 50 {:ok, opts} =
50 − Keyword.validate(opts, [:step, :step_opts, :name, single: false, persist: false, count: 300])
51 + Keyword.validate(opts, [
52 + :step,
53 + :step_opts,
54 + :name,
55 + single: false,
56 + persist: false,
57 + count: 300,
58 + speed: 1000
59 + ])
51 60
52 61 cnt = opts[:count]
53 62
54 63 specs =
55 64 case opts[:single] do
56 − # only start one, and then step & expand to meet cnt
65 + # start one, and then step & expand to meet cnt
57 66 true ->
58 67 [
59 68 Parent.child_spec(
@@ -71,6 +80,19 @@ defmodule LambentEx.Machine do
71 80 id: id
72 81 )
73 82 end)
83 +
84 + # start one, with the count passed in and note that it returns full arrays
85 + :full ->
86 + [
87 + Parent.child_spec(
88 + {opts[:step],
89 + opts[:step_opts]
90 + |> Map.put(:id, 0)
91 + |> Map.put(:name, opts[:name])
92 + |> Map.put(:count, cnt)},
93 + id: 0
94 + )
95 + ]
74 96 end
75 97
76 98 pids =
@@ -85,7 +107,7 @@ defmodule LambentEx.Machine do
85 107 steps: pids,
86 108 name: opts[:name],
87 109 # todo configurable
88 − speed: 1000,
110 + speed: opts[:speed],
89 111 bright_tgt: 255,
90 112 bright_curr: 255,
91 113 bright_delay: 0,
@@ -94,7 +116,11 @@ defmodule LambentEx.Machine do
94 116 single: opts[:single],
95 117 started: DateTime.utc_now(),
96 118 persist: opts[:persist],
119 + # modulating publishes
97 120 modpub: 0,
121 + # stored_keys for single: :full
122 + fullkeys: [],
123 + # load opts
98 124 full_opts: opts
99 125 }}
100 126 end
@@ -129,12 +155,36 @@ defmodule LambentEx.Machine do
129 155
130 156 # impls
131 157
158 + defp bmath({key, i}, state) do
159 + [{key, bmath(i, state)}] |> Map.new()
160 + end
161 +
162 + defp bmath(i, state) when is_map(i) do
163 + i |> Enum.map(fn {k, x} -> {k, bmath(x, state)} end) |> Map.new()
164 + end
165 +
166 + defp bmath(i, state) when is_list(i) do
167 + i |> Enum.map(fn v -> bmath(v, state) end)
168 + end
169 +
132 170 defp bmath(i, state) do
133 171 (i * state.bright_curr / 255.0) |> round
134 172 end
135 173
136 174 def stripstep(step) do
137 − [_head, step] = step |> to_string |> String.split(".Steps.")
175 + s = step |> to_string
176 +
177 + step =
178 + cond do
179 + String.contains?(s, "Steps") ->
180 + [_head, step] = step |> to_string |> String.split(".Steps.")
181 + step
182 + String.contains?(s, "Ext") ->
183 + [_head, step] = step |> to_string |> String.split(".Ext.")
184 + step
185 + end
186 +
187 +
138 188 step
139 189 end
140 190
@@ -143,6 +193,11 @@ defmodule LambentEx.Machine do
143 193 [step, "x", state[:name]] |> Enum.join("")
144 194 end
145 195
196 + defp id(state, key) do
197 + step = state[:step] |> stripstep
198 + [step, "x", state[:name], "x", key] |> Enum.join("")
199 + end
200 +
146 201 defp publish(state) do
147 202 %{
148 203 id: id(state),
@@ -157,6 +212,22 @@ defmodule LambentEx.Machine do
157 212 }
158 213 end
159 214
215 + defp publish(state, key) do
216 + key = Atom.to_string(key)
217 +
218 + %{
219 + id: id(state, key),
220 + name: state[:name] <> key,
221 + step: state[:step] |> stripstep,
222 + opts: state[:full_opts] |> Keyword.update(:persist, true, fn _ -> state[:persist] end),
223 + speed: state[:speed],
224 + cnt: state[:cnt],
225 + bgt: state[:bright_curr],
226 + bgtt: state[:bright_tgt],
227 + persist: state[:persist]
228 + }
229 + end
230 +
160 231 @impl true
161 232 def handle_info(:step, state) do
162 233 Process.send_after(self(), :step, state.speed)
@@ -171,19 +242,62 @@ defmodule LambentEx.Machine do
171 242
172 243 data =
173 244 case state[:single] do
174 − # expand single to fill cnt
175 − true ->
176 − data |> List.flatten() |> Stream.cycle() |> Enum.take(state[:cnt] * 3) |> Enum.chunk_every(3)
245 + :full ->
246 + data
247 + |> List.flatten()
248 + |> Enum.reduce(fn k, v -> Map.merge(k, v, fn _k, v1, v2 -> v2 ++ v1 end) end)
177 249
178 − # do nothing
179 − false ->
250 + _otherwise ->
180 251 data
181 252 end
182 253
183 − Phoenix.PubSub.broadcast(@pubsub_name, @pubsub_topic <> state[:name], {:publish, data})
254 + case state[:single] do
255 + # expand single to fill cnt
256 + true ->
257 + data =
258 + data
259 + |> List.flatten()
260 + |> Stream.cycle()
261 + |> Enum.take(state[:cnt] * 3)
262 + |> Enum.chunk_every(3)
263 +
264 + Phoenix.PubSub.broadcast(@pubsub_name, @pubsub_topic <> state[:name], {:publish, data})
265 +
266 + # do nothing
267 + false ->
268 + data
269 +
270 + Phoenix.PubSub.broadcast(@pubsub_name, @pubsub_topic <> state[:name], {:publish, data})
271 +
272 + :full ->
273 + # Bit hacky
274 + data
275 + |> Enum.map(fn {k, datum} ->
276 + Phoenix.PubSub.broadcast(
277 + @pubsub_name,
278 + # yolo etc
279 + @pubsub_topic <> state[:name] <> Atom.to_string(k),
280 + {:publish, datum}
281 + )
282 + end)
283 + end
284 +
285 + # Phoenix.PubSub.broadcast(@pubsub_name, @pubsub_topic <> state[:name], {:publish, data})
184 286
185 287 state =
186 288 cond do
289 + state[:single] == :full ->
290 + data
291 + |> Enum.map(fn {k, datum} ->
292 + Phoenix.PubSub.broadcast(
293 + @pubsub_name,
294 + @pubsub_topic_fh,
295 + {:firehose, {state[:name] <> Atom.to_string(k), datum |> Enum.slice(0..31)}}
296 + )
297 + end)
298 +
299 + state
300 +
187 301 state[:speed] < 500 ->
188 302 case 250 / state[:speed] <= state[:modpub] do
189 303 true ->
@@ -192,6 +306,7 @@ defmodule LambentEx.Machine do
192 306 @pubsub_topic_fh,
193 307 {:firehose, {state[:name], data |> Enum.slice(0..31)}}
194 308 )
309 +
195 310 state |> Map.put(:modpub, 0)
196 311
197 312 false ->
@@ -208,13 +323,48 @@ defmodule LambentEx.Machine do
208 323 state
209 324 end
210 325
211 − {:noreply, bright_step(state)}
326 + case state[:single] do
327 + :full ->
328 + case state[:fullkeys] do
329 + val when length(val) == 0 ->
330 + {:noreply,
331 + bright_step(
332 + state
333 + |> Map.put(
334 + :fullkeys,
335 + data
336 + |> Map.keys()
337 + )
338 + )}
339 +
340 + _otherwise ->
341 + {:noreply, bright_step(state)}
342 + end
343 +
344 + _otherwise ->
345 + {:noreply, bright_step(state)}
346 + end
212 347 end
213 348
214 349 @impl true
215 350 def handle_info(:publish, state) do
216 351 Process.send_after(self(), :publish, 400)
217 − Phoenix.PubSub.broadcast(@pubsub_name, @pubsub_topic_idx, {:machines_pub, publish(state)})
352 +
353 + case state[:single] do
354 + :full ->
355 + state[:fullkeys]
356 + |> Enum.map(fn k ->
357 + Phoenix.PubSub.broadcast(
358 + @pubsub_name,
359 + @pubsub_topic_idx,
360 + {:machines_pub, publish(state, k)}
361 + )
362 + end)
363 +
364 + _otherwise ->
365 + Phoenix.PubSub.broadcast(@pubsub_name, @pubsub_topic_idx, {:machines_pub, publish(state)})
366 + end
367 +
218 368 {:noreply, state}
219 369 end
220 370
added lib/lambent_ex/machine/ext/gsi.ex
+247 −0
@@ -0,0 +1,247 @@
1 +defmodule LambentEx.Machine.Ext.GSI.Cfg do
2 + # @enforce_keys [:h]
3 + defstruct [:src, :game, :count, :name, id: 0, flash_mticks: 8, flash_cticks: %{}]
4 +end
5 +
6 +defmodule LambentEx.Machine.Ext.GSI do
7 + @moduledoc false
8 + use GenServer
9 +
10 + @registry :lambent_steps
11 + @cls :gsi
12 +
13 + @pubsub_name LambentEx.PubSub
14 + @pubsub_topic "http_pubs"
15 +
16 + @red [200, 0, 0]
17 + @green [0, 200, 0]
18 + @blue [0, 0, 200]
19 + @yellow [180, 180, 0]
20 +
21 + @black [0, 0, 0]
22 + @blackish [15, 15, 15]
23 + @whitish [200, 200, 200]
24 + @csgo_blue [93, 121, 174]
25 + @csgo_orange [222, 155, 53]
26 + @csgo_tan [204, 186, 124]
27 + @csgo_money [77, 119, 41]
28 +
29 + @csgo_max_money 10000
30 + @csgo_max_health 100
31 +
32 + alias LambentEx.Machine.Ext.GSI.Cfg
33 + alias LambentEx.Utils.Color
34 +
35 + defp n(opts) do
36 + "#{@cls}-#{opts[:name]}-#{opts[:id]}"
37 + end
38 +
39 + def start_link(opts) do
40 + GenServer.start_link(__MODULE__, opts, name: via_tuple(n(opts)))
41 + end
42 +
43 + def init(opts) do
44 + opts = opts |> Map.put(:status, opts.id)
45 + cfg = struct(%Cfg{}, opts) |> Map.from_struct()
46 +
47 + Phoenix.PubSub.subscribe(@pubsub_name, @pubsub_topic)
48 +
49 + {:ok, cfg |> Map.put(:gs, %{})}
50 + end
51 +
52 + defp via_tuple(name) do
53 + {:via, Registry, {@registry, name}}
54 + end
55 +
56 + defp delta(data, state) do
57 + # IO.puts("delta")
58 + # IO.inspect(data, pretty: true)
59 + # IO.inspect(MapDiff.diff(data, state[:gs]), pretty: true)
60 + end
61 +
62 + defp current_team_color(state) do
63 + case read(:csgo_team, state) do
64 + "T" -> @csgo_orange
65 + "CT" -> @csgo_blue
66 + nil -> @csgo_tan
67 + end
68 + end
69 +
70 + defp generate_fill(state, color, nil) do
71 + []
72 + end
73 +
74 + defp generate_fill(state, color, %{min: min, max: max}),
75 + do: generate_fill(state, color, min, max)
76 +
77 + # fill
78 + defp generate_fill(state, color, min) do
79 + []
80 + end
81 +
82 + defp generate_fill(state, {color_low, color_high}, min, max) do
83 +# IO.inspect({color_low, color_high, min, max, state[:count]})
84 + qty = (min / max * state[:count]) |> trunc
85 + rem = state[:count] - qty
86 + colored = color_high |> Stream.cycle() |> Enum.take(qty * 3)
87 + background = color_low |> Stream.cycle() |> Enum.take(rem * 3)
88 + colored ++ background
89 + end
90 +
91 + defp generate_fill(state, color, min, max) do
92 + # IO.inspect({color, min, max, state[:count]})
93 + qty = (min / max * state[:count]) |> trunc
94 + rem = state[:count] - qty
95 + colored = color |> Stream.cycle() |> Enum.take(qty * 3)
96 + background = @blackish |> Stream.cycle() |> Enum.take(rem * 3)
97 + colored ++ background
98 + end
99 +
100 + defp generate_bright(state, {color_low, color_high}, min, max) do
101 + []
102 + end
103 +
104 + defp generate_bright(state, color, %{min: min, max: max}),
105 + do: generate_bright(state, color, min, max)
106 +
107 + defp generate_bright(state, color, min, max) do
108 + []
109 + end
110 +
111 + defp read(:csgo_team, state), do: state[:gs] |> get_in(["player", "team"])
112 + defp read(:csgo_health, state), do: state[:gs] |> get_in(["player", "state", "health"]) || 0
113 + defp read(:csgo_armor, state), do: state[:gs] |> get_in(["player", "state", "armor"]) || 0
114 + defp read(:csgo_money, state), do: state[:gs] |> get_in(["player", "state", "money"]) || 0
115 + defp read(:csgo_kills, state), do: state[:gs] |> get_in(["player", "match_stats", "kills"]) || 0
116 +
117 + defp read(:csgo_deaths, state),
118 + do: state[:gs] |> get_in(["player", "match_stats", "deaths"]) || 0
119 +
120 + defp read(:csgo_assists, state),
121 + do: state[:gs] |> get_in(["player", "match_stats", "assists"]) || 0
122 +
123 + defp read(:csgo_mvps, state), do: state[:gs] |> get_in(["player", "match_stats", "mvps"]) || 0
124 + defp read(:csgo_score, state), do: state[:gs] |> get_in(["player", "match_stats", "score"]) || 0
125 + defp read(:csgo_wins_t, state), do: state[:gs] |> get_in(["map", "team_t", "score"]) || 0
126 + defp read(:csgo_wins_ct, state), do: state[:gs] |> get_in(["map", "team_ct", "score"]) || 0
127 + defp read(:csgo_flash, state), do: state[:gs] |> get_in(["player", "state", "flashed"]) || 0
128 + defp read(:csgo_burn, state), do: state[:gs] |> get_in(["player", "state", "burning"]) || 0
129 + defp read(:csgo_smoke, state), do: state[:gs] |> get_in(["player", "state", "smoked"]) || 0
130 +
131 + # complexer
132 + defp read(:csgo_bullets, state) do
133 + case state[:gs] do
134 + val when map_size(val) == 0 ->
135 + %{min: 0, max: state[:count]}
136 +
137 + _otherwise ->
138 + {_idx, weapon} =
139 + state[:gs]
140 + |> get_in(["player", "weapons"])
141 + |> Enum.filter(fn {_k, v} -> Enum.member?(["active", "reloading"], v["state"]) end)
142 + |> List.first()
143 +
144 + cond do
145 + weapon == %{} ->
146 + %{min: 0, max: state[:count]}
147 +
148 + weapon["type"] == "Knife" ->
149 + %{min: 0, max: state[:count]}
150 +
151 + weapon["type"] == "C4" ->
152 + %{min: 1, max: 1}
153 +
154 + true ->
155 + %{min: weapon["ammo_clip"], max: weapon["ammo_clip_max"]}
156 + end
157 + end
158 + end
159 +
160 + defp read(:csgo_wins_curr, state) do
161 + case read(:csgo_team, state) do
162 + "T" -> read(:csgo_wins_t, state)
163 + "CT" -> read(:csgo_wins_ct, state)
164 + nil -> 0
165 + end
166 + end
167 +
168 + defp read(:csgo_bomb, state), do: state[:gs]
169 +
170 + defp read(:csgo, state) do
171 + %{
172 + health_fill:
173 + generate_fill(state, {@red, @whitish}, read(:csgo_health, state), @csgo_max_health),
174 + health_bright:
175 + generate_bright(state, {@red, @whitish}, read(:csgo_health, state), @csgo_max_health),
176 + armor_fill:
177 + generate_fill(state, {@red, @whitish}, read(:csgo_armor, state), @csgo_max_health),
178 + armor_bright:
179 + generate_bright(state, {@red, @whitish}, read(:csgo_armor, state), @csgo_max_health),
180 + money_fill: generate_fill(state, @csgo_money, read(:csgo_money, state), @csgo_max_money),
181 + money_bright:
182 + generate_bright(state, @csgo_money, read(:csgo_money, state), @csgo_max_money),
183 + bullets_fill: generate_fill(state, current_team_color(state), read(:csgo_bullets, state)),
184 + bullets_bright: generate_bright(state, @csgo_tan, read(:csgo_bullets, state)),
185 + kills_fill: generate_fill(state, current_team_color(state), read(:csgo_kills, state)),
186 + deaths_fill: generate_fill(state, current_team_color(state), read(:csgo_deaths, state)),
187 + assists_fill: generate_fill(state, current_team_color(state), read(:csgo_assists, state)),
188 + mvps_fill: generate_fill(state, current_team_color(state), read(:csgo_mvps, state)),
189 + score_fill: generate_fill(state, current_team_color(state), read(:csgo_score, state)),
190 + wins_t_fill: generate_fill(state, @csgo_orange, read(:csgo_wins_t, state), 13),
191 + wins_ct_fill: generate_fill(state, @csgo_blue, read(:csgo_wins_ct, state), 13),
192 + wins_curr_fill: generate_fill(state, @csgo_blue, read(:csgo_wins_curr, state), 13),
193 + round_wins_fill: [],
194 + team_fill: [],
195 + bomb_flash: [],
196 + flash_flash: generate_bright(state, @whitish, read(:csgo_flash, state), 1),
197 + burn_flash: generate_bright(state, @red, read(:csgo_burn, state), 1),
198 + smoke_flash: generate_bright(state, @csgo_tan, read(:csgo_smoke, state), 1),
199 + combined: []
200 + }
201 + end
202 +
203 + defp read(:dota, state) do
204 + %{
205 + health_fill: [],
206 + health_bright: [],
207 + mana_fill: [],
208 + mana_bright: [],
209 + money_fill: [],
210 + money_bright: []
211 + }
212 + end
213 +
214 + defp read(state) do
215 + case state[:gs] do
216 + nil ->
217 + %{}
218 +
219 + otherwise ->
220 + case state[:game] do
221 + :dota -> read(:dota, state)
222 + :csgo -> read(:csgo, state)
223 + end
224 + end
225 + end
226 +
227 + def handle_cast(:step, state) do
228 + # todo tick down flashers here
229 + {:noreply, state}
230 + end
231 +
232 + def handle_call(:read, _from, state) do
233 + {:reply, read(state), state}
234 + end
235 +
236 + def handle_info({name, path, data}, state) do
237 + IO.inspect({name, state[:name]})
238 +
239 + if name == state[:name] do
240 + # IO.inspect(data)
241 + flashes = delta(data, state)
242 + {:noreply, state |> Map.put(:path, path) |> Map.put(:gs, data)}
243 + else
244 + {:noreply, state}
245 + end
246 + end
247 +end
modified lib/lambent_ex/meta.ex
+39 −0
@@ -3,6 +3,7 @@ defmodule LambentEx.Meta do
3 3
4 4 @table :lex_metadata
5 5 @pubsub_name LambentEx.PubSub
6 + @pubsub_svc_http "svc_pub"
6 7
7 8 def init(arg) do
8 9 # File.cd("meta")
@@ -14,6 +15,7 @@ defmodule LambentEx.Meta do
14 15 Phoenix.PubSub.subscribe(@pubsub_name, "mqtt_idx")
15 16 Process.send_after(self(), :machine_flush, 5000)
16 17 Process.send_after(self(), :link_flush, 5000)
18 + Process.send_after(self(), :svc_http_pub, 10)
17 19 ml = MapSet.new()
18 20 ll = MapSet.new()
19 21
@@ -67,6 +69,36 @@ defmodule LambentEx.Meta do
67 69 :dets.insert(@table, {"place-#{key}", value})
68 70 end
69 71
72 + # svcs
73 + def put_http(name), do: put_http(name, Ecto.UUID.generate)
74 + def put_http(name, path) do
75 + curr = get_http
76 +
77 + case length(curr) do
78 + 0 -> :dets.insert(@table, {"http", [{name, path}]})
79 + _otherwise -> :dets.insert(@table, {"http", curr ++ [{name, path}]})
80 + end
81 + end
82 +
83 + def del_http(key) do
84 + to_delete = get_http |> Enum.filter(fn {k, _v} -> k == key end)
85 +
86 + :dets.insert(
87 + @table,
88 + {"http", to_delete |> Enum.reduce(get_http, fn x, acc -> acc |> List.delete(x) end)}
89 + )
90 + end
91 +
92 + def get_http do
93 + case :dets.lookup(@table, "http") do
94 + [] ->
95 + []
96 +
97 + [{_key, value}] ->
98 + value
99 + end
100 + end
101 +
70 102 # init functions
71 103 def get_saved_machines do
72 104 case :dets.lookup(@table, "machs") do
@@ -146,6 +178,13 @@ defmodule LambentEx.Meta do
146 178 end
147 179 end
148 180
181 + def handle_info(:svc_http_pub, state) do
182 + Process.send_after(self(), :svc_http_pub, 500)
183 +
184 + Phoenix.PubSub.broadcast(@pubsub_name, @pubsub_svc_http, {:http, get_http})
185 + {:noreply, state}
186 + end
187 +
149 188 def handle_info({:machines_pub, machine}, state) do
150 189 case machine[:persist] do
151 190 true ->
added lib/lambent_ex_web/controllers/data_controller.ex
+39 −0

Click to load diff…

added lib/lambent_ex_web/controllers/fallback_controller.ex
+16 −0

Click to load diff…

modified lib/lambent_ex_web/live/machines/index.ex
+2 −0

Click to load diff…

modified lib/lambent_ex_web/live/services/index.ex
+5 −14

Click to load diff…

modified lib/lambent_ex_web/live/services/index.html.heex
+8 −1

Click to load diff…

modified lib/lambent_ex_web/router.ex
+4 −3

Click to load diff…

Parents: 3ff7ac5