Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0cddc33ac6 | ||
|
|
a1a12b1873 | ||
|
|
ae1e32391c | ||
|
|
97cc3ae151 | ||
|
|
2113252e2d | ||
|
|
2cae3c6d28 | ||
|
|
de261ae400 | ||
|
|
3dd0a557d4 | ||
|
|
51ae1b27b4 | ||
|
|
50e89492cf | ||
|
|
3d9ea1296c | ||
|
|
db69f76aa0 | ||
|
|
0ee3a8b6e8 | ||
|
|
b379f1d82c | ||
|
|
c83f9d8700 | ||
|
|
6289222b69 | ||
|
|
607a65e22e | ||
|
|
6bbb297c59 |
1
go.mod
1
go.mod
@@ -5,4 +5,5 @@ go 1.19
|
|||||||
require (
|
require (
|
||||||
github.com/go-yaml/yaml v2.1.0+incompatible // indirect
|
github.com/go-yaml/yaml v2.1.0+incompatible // indirect
|
||||||
github.com/micmonay/keybd_event v1.1.1 // indirect
|
github.com/micmonay/keybd_event v1.1.1 // indirect
|
||||||
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
3
go.sum
3
go.sum
@@ -2,3 +2,6 @@ github.com/go-yaml/yaml v2.1.0+incompatible h1:RYi2hDdss1u4YE7GwixGzWwVo47T8UQwn
|
|||||||
github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0=
|
github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0=
|
||||||
github.com/micmonay/keybd_event v1.1.1 h1:rv7omwXWYL9Lgf3PUq6uBgJI2k1yGkL/GD6dxc6nmSs=
|
github.com/micmonay/keybd_event v1.1.1 h1:rv7omwXWYL9Lgf3PUq6uBgJI2k1yGkL/GD6dxc6nmSs=
|
||||||
github.com/micmonay/keybd_event v1.1.1/go.mod h1:CGMWMDNgsfPljzrAWoybUOSKafQPZpv+rLigt2LzNGI=
|
github.com/micmonay/keybd_event v1.1.1/go.mod h1:CGMWMDNgsfPljzrAWoybUOSKafQPZpv+rLigt2LzNGI=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||||
|
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
export DEBUG=true
|
export DEBUG=true
|
||||||
export RAW_UDP=17070
|
export RAW_UDP=17070
|
||||||
export BUTTON_PARSER_V01=true
|
export BUTTON_V01=true
|
||||||
export WRAP_REFRESH_ON_SIGUSR1=true
|
export WRAP_REFRESH_ON_SIGUSR1=true
|
||||||
export MAIN_INTERVAL_DURATION=5ms
|
export MAIN_INTERVAL_DURATION=5ms
|
||||||
export OUTPUT_KEYBOARD=false
|
export OUTPUT_KEYBOARD=false
|
||||||
|
|||||||
@@ -6,14 +6,19 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
FlagButtonV01 = os.Getenv("BUTTON_V01") == "true"
|
||||||
|
)
|
||||||
|
|
||||||
type Parser interface {
|
type Parser interface {
|
||||||
Read() []Button
|
Read() []Button
|
||||||
Close()
|
Close()
|
||||||
|
CloseWrap() raw.Raw
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(ctx context.Context, src raw.Raw) Parser {
|
func New(ctx context.Context, src raw.Raw) Parser {
|
||||||
if os.Getenv("BUTTON_PARSER_V01") == "true" {
|
if FlagButtonV01 {
|
||||||
return NewV01(src)
|
return NewV01(ctx, src)
|
||||||
}
|
}
|
||||||
return NewPlaintext(src)
|
return NewPlaintext(src)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,10 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
FlagButtonPlaintextRelease = os.Getenv("BUTTON_PLAINTEXT_RELEASE")
|
||||||
|
)
|
||||||
|
|
||||||
type Plaintext struct {
|
type Plaintext struct {
|
||||||
src raw.Raw
|
src raw.Raw
|
||||||
release byte
|
release byte
|
||||||
@@ -12,8 +16,8 @@ type Plaintext struct {
|
|||||||
|
|
||||||
func NewPlaintext(src raw.Raw) Plaintext {
|
func NewPlaintext(src raw.Raw) Plaintext {
|
||||||
releaseChar := byte('!')
|
releaseChar := byte('!')
|
||||||
if v := os.Getenv("BUTTON_PLAINTEXT_RELEASE"); v != "" {
|
if FlagButtonPlaintextRelease != "" {
|
||||||
releaseChar = byte(v[0])
|
releaseChar = byte(FlagButtonPlaintextRelease[0])
|
||||||
}
|
}
|
||||||
return Plaintext{
|
return Plaintext{
|
||||||
src: src,
|
src: src,
|
||||||
@@ -23,6 +27,8 @@ func NewPlaintext(src raw.Raw) Plaintext {
|
|||||||
|
|
||||||
func (p Plaintext) Close() { p.src.Close() }
|
func (p Plaintext) Close() { p.src.Close() }
|
||||||
|
|
||||||
|
func (p Plaintext) CloseWrap() raw.Raw { return p.src }
|
||||||
|
|
||||||
func (p Plaintext) Read() []Button {
|
func (p Plaintext) Read() []Button {
|
||||||
b := p.src.Read()
|
b := p.src.Read()
|
||||||
buttons := make([]Button, 0, len(b))
|
buttons := make([]Button, 0, len(b))
|
||||||
|
|||||||
16
src/device/input/button/testdata/v01.yaml
vendored
Normal file
16
src/device/input/button/testdata/v01.yaml
vendored
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
users:
|
||||||
|
bel:
|
||||||
|
player: 0
|
||||||
|
message: "hi"
|
||||||
|
players:
|
||||||
|
- buttons:
|
||||||
|
up: "w"
|
||||||
|
down: "s"
|
||||||
|
left: "a"
|
||||||
|
right: "d"
|
||||||
|
l: "q"
|
||||||
|
r: "e"
|
||||||
|
a: "1"
|
||||||
|
b: "2"
|
||||||
|
x: "3"
|
||||||
|
y: "4"
|
||||||
@@ -1,17 +1,28 @@
|
|||||||
package button
|
package button
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"mayhem-party/src/device/input/raw"
|
"mayhem-party/src/device/input/raw"
|
||||||
"os"
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"gopkg.in/yaml.v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
var debugging = os.Getenv("DEBUG") == "true"
|
var (
|
||||||
|
FlagDebug = os.Getenv("DEBUG") == "true"
|
||||||
|
FlagButtonV01Config = os.Getenv("BUTTON_V01_CONFIG")
|
||||||
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
V01 struct {
|
V01 struct {
|
||||||
|
ctx context.Context
|
||||||
|
can context.CancelFunc
|
||||||
src raw.Raw
|
src raw.Raw
|
||||||
|
cfg v01Cfg
|
||||||
}
|
}
|
||||||
v01Msg struct {
|
v01Msg struct {
|
||||||
T int64
|
T int64
|
||||||
@@ -19,15 +30,38 @@ type (
|
|||||||
Y string
|
Y string
|
||||||
N string
|
N string
|
||||||
}
|
}
|
||||||
|
v01Cfg struct {
|
||||||
|
Users map[string]struct {
|
||||||
|
Player int
|
||||||
|
Message string
|
||||||
|
}
|
||||||
|
Players []struct {
|
||||||
|
Transformation v01Transformation
|
||||||
|
}
|
||||||
|
}
|
||||||
|
v01Transformation map[string]string
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewV01(src raw.Raw) V01 {
|
func NewV01(ctx context.Context, src raw.Raw) V01 {
|
||||||
|
var cfg v01Cfg
|
||||||
|
b, _ := ioutil.ReadFile(FlagButtonV01Config)
|
||||||
|
yaml.Unmarshal(b, &cfg)
|
||||||
|
ctx, can := context.WithCancel(ctx)
|
||||||
return V01{
|
return V01{
|
||||||
|
ctx: ctx,
|
||||||
|
can: can,
|
||||||
src: src,
|
src: src,
|
||||||
|
cfg: cfg,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (v01 V01) CloseWrap() raw.Raw {
|
||||||
|
v01.can()
|
||||||
|
return v01.src
|
||||||
|
}
|
||||||
|
|
||||||
func (v01 V01) Close() {
|
func (v01 V01) Close() {
|
||||||
|
v01.can()
|
||||||
v01.src.Close()
|
v01.src.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,7 +71,40 @@ func (v01 V01) Read() []Button {
|
|||||||
if err := json.Unmarshal(line, &msg); err != nil {
|
if err := json.Unmarshal(line, &msg); err != nil {
|
||||||
log.Printf("%v: %s", err, line)
|
log.Printf("%v: %s", err, line)
|
||||||
}
|
}
|
||||||
return msg.buttons()
|
v01.telemetry(msg)
|
||||||
|
|
||||||
|
return v01.cfg.transform(msg).buttons()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cfg v01Cfg) transform(msg v01Msg) v01Msg {
|
||||||
|
if len(cfg.Players) == 0 {
|
||||||
|
return msg
|
||||||
|
}
|
||||||
|
user := cfg.Users[msg.U]
|
||||||
|
if user.Player < 1 {
|
||||||
|
msg.Y = ""
|
||||||
|
msg.N = ""
|
||||||
|
return msg
|
||||||
|
}
|
||||||
|
player := cfg.Players[user.Player-1]
|
||||||
|
msg.Y = player.Transformation.pipe(msg.Y)
|
||||||
|
msg.N = player.Transformation.pipe(msg.N)
|
||||||
|
return msg
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t v01Transformation) pipe(s string) string {
|
||||||
|
for i := range s {
|
||||||
|
if v := t[s[i:i+1]]; v != "" {
|
||||||
|
s = s[:i] + v[:1] + s[i+1:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v01 V01) telemetry(msg v01Msg) {
|
||||||
|
if FlagDebug {
|
||||||
|
log.Printf("%s|%dms", msg.U, time.Now().UnixNano()/int64(time.Millisecond)-msg.T)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (msg v01Msg) buttons() []Button {
|
func (msg v01Msg) buttons() []Button {
|
||||||
@@ -48,7 +115,7 @@ func (msg v01Msg) buttons() []Button {
|
|||||||
for i := range msg.N {
|
for i := range msg.N {
|
||||||
buttons[len(msg.Y)+i] = Button{Char: msg.N[i], Down: false}
|
buttons[len(msg.Y)+i] = Button{Char: msg.N[i], Down: false}
|
||||||
}
|
}
|
||||||
if debugging {
|
if FlagDebug {
|
||||||
log.Printf("%+v", msg)
|
log.Printf("%+v", msg)
|
||||||
}
|
}
|
||||||
return buttons
|
return buttons
|
||||||
|
|||||||
72
src/device/input/button/v01_exported_test.go
Normal file
72
src/device/input/button/v01_exported_test.go
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
package button_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"mayhem-party/src/device/input/button"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestV01(t *testing.T) {
|
||||||
|
src := constSrc(fmt.Sprintf(`{"T":%v,"U":"bel","Y":"abc","N":"cde"}`, time.Now().UnixNano()/int64(time.Millisecond)-50))
|
||||||
|
t.Logf("(%v) %s", len(src), src.Read())
|
||||||
|
v01 := button.NewV01(context.Background(), src)
|
||||||
|
got := v01.Read()
|
||||||
|
want := []button.Button{
|
||||||
|
{Down: true, Char: 'a'},
|
||||||
|
{Down: true, Char: 'b'},
|
||||||
|
{Down: true, Char: 'c'},
|
||||||
|
{Down: false, Char: 'c'},
|
||||||
|
{Down: false, Char: 'd'},
|
||||||
|
{Down: false, Char: 'e'},
|
||||||
|
}
|
||||||
|
if len(got) != len(want) {
|
||||||
|
t.Fatal(len(want), len(got))
|
||||||
|
}
|
||||||
|
for i := range got {
|
||||||
|
if got[i] != want[i] {
|
||||||
|
t.Errorf("[%d] want %+v got %+v", i, want[i], got[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestV01WithCfg(t *testing.T) {
|
||||||
|
d := t.TempDir()
|
||||||
|
p := path.Join(d, "cfg.yaml")
|
||||||
|
os.WriteFile(p, []byte(`
|
||||||
|
users:
|
||||||
|
bel:
|
||||||
|
player: 2
|
||||||
|
players:
|
||||||
|
- transformation:
|
||||||
|
w: t
|
||||||
|
- transformation:
|
||||||
|
w: i
|
||||||
|
`), os.ModePerm)
|
||||||
|
button.FlagButtonV01Config = p
|
||||||
|
|
||||||
|
t.Run("unknown user ignored", func(t *testing.T) {
|
||||||
|
v01 := button.NewV01(context.Background(), constSrc(`{"U":"qt","Y":"w"}`))
|
||||||
|
got := v01.Read()
|
||||||
|
if len(got) != 0 {
|
||||||
|
t.Error(got)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("player2", func(t *testing.T) {
|
||||||
|
v01 := button.NewV01(context.Background(), constSrc(`{"U":"bel","Y":"w","N":"w"}`))
|
||||||
|
got := v01.Read()
|
||||||
|
if len(got) != 2 {
|
||||||
|
t.Error(got)
|
||||||
|
}
|
||||||
|
if got[0] != (button.Button{Char: 'i', Down: true}) {
|
||||||
|
t.Error(got[0])
|
||||||
|
}
|
||||||
|
if got[1] != (button.Button{Char: 'i', Down: false}) {
|
||||||
|
t.Error(got[1])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -1,29 +1,51 @@
|
|||||||
package button_test
|
package button
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"mayhem-party/src/device/input/button"
|
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestV01(t *testing.T) {
|
func TestV01TransformationPipe(t *testing.T) {
|
||||||
src := constSrc(`{"T":1,"U":"bel","Y":"abc","N":"cde"}`)
|
cases := map[string]struct {
|
||||||
t.Logf("(%v) %s", len(src), src.Read())
|
input string
|
||||||
v01 := button.NewV01(src)
|
xform map[string]string
|
||||||
got := v01.Read()
|
want string
|
||||||
want := []button.Button{
|
}{
|
||||||
{Down: true, Char: 'a'},
|
"empty input": {
|
||||||
{Down: true, Char: 'b'},
|
xform: map[string]string{"a": "bc"},
|
||||||
{Down: true, Char: 'c'},
|
},
|
||||||
{Down: false, Char: 'c'},
|
"empty xform": {
|
||||||
{Down: false, Char: 'd'},
|
input: "aa",
|
||||||
{Down: false, Char: 'e'},
|
want: "aa",
|
||||||
|
},
|
||||||
|
"all": {
|
||||||
|
input: "aa",
|
||||||
|
xform: map[string]string{"a": "cc"},
|
||||||
|
want: "cc",
|
||||||
|
},
|
||||||
|
"last": {
|
||||||
|
input: "ba",
|
||||||
|
xform: map[string]string{"a": "cc"},
|
||||||
|
want: "bc",
|
||||||
|
},
|
||||||
|
"first": {
|
||||||
|
input: "ab",
|
||||||
|
xform: map[string]string{"a": "cc"},
|
||||||
|
want: "cb",
|
||||||
|
},
|
||||||
|
"noop": {
|
||||||
|
input: "bb",
|
||||||
|
xform: map[string]string{"a": "bc"},
|
||||||
|
want: "bb",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
if len(got) != len(want) {
|
|
||||||
t.Fatal(len(want), len(got))
|
for name, d := range cases {
|
||||||
}
|
c := d
|
||||||
for i := range got {
|
t.Run(name, func(t *testing.T) {
|
||||||
if got[i] != want[i] {
|
got := v01Transformation(c.xform).pipe(c.input)
|
||||||
t.Errorf("[%d] want %+v got %+v", i, want[i], got[i])
|
if got != c.want {
|
||||||
|
t.Errorf("%+v(%s) want %s got %s", c.xform, c.input, c.want, got)
|
||||||
}
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ package input_test
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"mayhem-party/src/device/input"
|
"mayhem-party/src/device/input"
|
||||||
|
"mayhem-party/src/device/input/raw"
|
||||||
|
"mayhem-party/src/device/input/wrap"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"testing"
|
"testing"
|
||||||
@@ -30,11 +32,11 @@ func TestNewRemapped(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
os.Setenv("WRAP_REMAP_FILE", remap)
|
wrap.FlagRemapFile = remap
|
||||||
os.Setenv("RAW_RANDOM_WEIGHT_FILE", rand)
|
raw.FlagRawRandomWeightFile = rand
|
||||||
t.Cleanup(func() {
|
t.Cleanup(func() {
|
||||||
os.Unsetenv("WRAP_REMAP_FILE")
|
wrap.FlagRemapFile = ""
|
||||||
os.Unsetenv("RAW_RANDOM_WEIGHT_FILE")
|
raw.FlagRawRandomWeightFile = ""
|
||||||
})
|
})
|
||||||
|
|
||||||
r := input.New(context.Background())
|
r := input.New(context.Background())
|
||||||
@@ -50,9 +52,9 @@ func TestNewRemapped(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestNewBuffered(t *testing.T) {
|
func TestNewBuffered(t *testing.T) {
|
||||||
os.Setenv("WRAP_BUFFERED", "true")
|
wrap.FlagBuffered = true
|
||||||
t.Cleanup(func() {
|
t.Cleanup(func() {
|
||||||
os.Unsetenv("WRAP_BUFFERED")
|
wrap.FlagBuffered = false
|
||||||
})
|
})
|
||||||
|
|
||||||
r := input.New(context.Background())
|
r := input.New(context.Background())
|
||||||
@@ -71,9 +73,9 @@ func TestNewRandomWeightFile(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
os.Setenv("RAW_RANDOM_WEIGHT_FILE", p)
|
raw.FlagRawRandomWeightFile = p
|
||||||
t.Cleanup(func() {
|
t.Cleanup(func() {
|
||||||
os.Unsetenv("RAW_RANDOM_WEIGHT_FILE")
|
raw.FlagRawRandomWeightFile = ""
|
||||||
})
|
})
|
||||||
|
|
||||||
r := input.New(context.Background())
|
r := input.New(context.Background())
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ func (kb Keyboard) Read() []byte {
|
|||||||
if err != nil && err != io.EOF {
|
if err != nil && err != io.EOF {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
if os.Getenv("DEBUG") == "true" {
|
if FlagDebug {
|
||||||
log.Printf("raw.Keyboard.Read() %s", b[:n])
|
log.Printf("raw.Keyboard.Read() %s", b[:n])
|
||||||
}
|
}
|
||||||
return b[:n]
|
return b[:n]
|
||||||
|
|||||||
@@ -6,21 +6,27 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
FlagRawKeyboard = os.Getenv("RAW_KEYBOARD") == "true"
|
||||||
|
FlagRawUDP = os.Getenv("RAW_UDP")
|
||||||
|
FlagRawRandomWeightFile = os.Getenv("RAW_RANDOM_WEIGHT_FILE")
|
||||||
|
)
|
||||||
|
|
||||||
type Raw interface {
|
type Raw interface {
|
||||||
Read() []byte
|
Read() []byte
|
||||||
Close()
|
Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(ctx context.Context) Raw {
|
func New(ctx context.Context) Raw {
|
||||||
if os.Getenv("RAW_KEYBOARD") == "true" {
|
if FlagRawKeyboard {
|
||||||
return NewKeyboard()
|
return NewKeyboard()
|
||||||
}
|
}
|
||||||
if port, _ := strconv.Atoi(os.Getenv("RAW_UDP")); port != 0 {
|
if port, _ := strconv.Atoi(FlagRawUDP); port != 0 {
|
||||||
return NewUDP(ctx, port)
|
return NewUDP(ctx, port)
|
||||||
}
|
}
|
||||||
generator := randomCharFromRange('a', 'g')
|
generator := randomCharFromRange('a', 'g')
|
||||||
if p, ok := os.LookupEnv("RAW_RANDOM_WEIGHT_FILE"); ok && len(p) > 0 {
|
if FlagRawRandomWeightFile != "" {
|
||||||
generator = randomCharFromWeightFile(p)
|
generator = randomCharFromWeightFile(FlagRawRandomWeightFile)
|
||||||
}
|
}
|
||||||
return NewRandom(generator)
|
return NewRandom(generator)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,10 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
FlagDebug = os.Getenv("DEBUG") == "true"
|
||||||
|
)
|
||||||
|
|
||||||
type UDP struct {
|
type UDP struct {
|
||||||
conn net.PacketConn
|
conn net.PacketConn
|
||||||
c chan []byte
|
c chan []byte
|
||||||
@@ -29,14 +33,13 @@ func NewUDP(ctx context.Context, port int) UDP {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (udp UDP) listen() {
|
func (udp UDP) listen() {
|
||||||
debugging := os.Getenv("DEBUG") == "true"
|
|
||||||
for udp.ctx.Err() == nil {
|
for udp.ctx.Err() == nil {
|
||||||
buff := make([]byte, 256)
|
buff := make([]byte, 256)
|
||||||
n, _, err := udp.conn.ReadFrom(buff)
|
n, _, err := udp.conn.ReadFrom(buff)
|
||||||
if err != nil && udp.ctx.Err() == nil {
|
if err != nil && udp.ctx.Err() == nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
if debugging {
|
if FlagDebug {
|
||||||
log.Printf("raw.UDP.Read() => %s", buff[:n])
|
log.Printf("raw.UDP.Read() => %s", buff[:n])
|
||||||
}
|
}
|
||||||
select {
|
select {
|
||||||
|
|||||||
@@ -3,11 +3,16 @@ package wrap
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"mayhem-party/src/device/input/button"
|
"mayhem-party/src/device/input/button"
|
||||||
|
"mayhem-party/src/device/input/raw"
|
||||||
"os"
|
"os"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
FlagBufferedStickyDuration = os.Getenv("WRAP_BUFFERED_STICKY_DURATION")
|
||||||
|
)
|
||||||
|
|
||||||
type Buffered struct {
|
type Buffered struct {
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
can context.CancelFunc
|
can context.CancelFunc
|
||||||
@@ -21,7 +26,7 @@ type Buffered struct {
|
|||||||
func NewBuffered(ctx context.Context, input Wrap) *Buffered {
|
func NewBuffered(ctx context.Context, input Wrap) *Buffered {
|
||||||
ctx, can := context.WithCancel(ctx)
|
ctx, can := context.WithCancel(ctx)
|
||||||
expirationInterval := time.Millisecond * 125
|
expirationInterval := time.Millisecond * 125
|
||||||
if d, err := time.ParseDuration(os.Getenv("WRAP_BUFFERED_STICKY_DURATION")); err == nil {
|
if d, err := time.ParseDuration(FlagBufferedStickyDuration); err == nil {
|
||||||
expirationInterval = d
|
expirationInterval = d
|
||||||
}
|
}
|
||||||
result := &Buffered{
|
result := &Buffered{
|
||||||
@@ -57,6 +62,11 @@ func (b *Buffered) listen() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *Buffered) CloseWrap() raw.Raw {
|
||||||
|
b.can()
|
||||||
|
return b.input.CloseWrap()
|
||||||
|
}
|
||||||
|
|
||||||
func (b *Buffered) Close() {
|
func (b *Buffered) Close() {
|
||||||
b.input.Close()
|
b.input.Close()
|
||||||
b.can()
|
b.can()
|
||||||
|
|||||||
@@ -1,24 +0,0 @@
|
|||||||
package wrap
|
|
||||||
|
|
||||||
import (
|
|
||||||
"mayhem-party/src/device/input/button"
|
|
||||||
"mayhem-party/src/device/input/raw"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Protocol struct {
|
|
||||||
src raw.Raw
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewProtocol(src raw.Raw) Protocol {
|
|
||||||
return Protocol{
|
|
||||||
src: src,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p Protocol) Close() {
|
|
||||||
p.src.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p Protocol) Read() []button.Button {
|
|
||||||
panic(nil)
|
|
||||||
}
|
|
||||||
@@ -4,8 +4,18 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"log"
|
"log"
|
||||||
"mayhem-party/src/device/input/button"
|
"mayhem-party/src/device/input/button"
|
||||||
|
"mayhem-party/src/device/input/raw"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
chSigUsr1 = func() chan os.Signal {
|
||||||
|
c := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(c, syscall.SIGUSR1)
|
||||||
|
return c
|
||||||
|
}()
|
||||||
)
|
)
|
||||||
|
|
||||||
type Refresh struct {
|
type Refresh struct {
|
||||||
@@ -13,14 +23,12 @@ type Refresh struct {
|
|||||||
input Wrap
|
input Wrap
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRefreshCh(sig os.Signal) <-chan os.Signal {
|
func NewRefresh(ctx context.Context, newWrap func() Wrap) *Refresh {
|
||||||
c := make(chan os.Signal, 1)
|
return NewRefreshWith(ctx, newWrap, chSigUsr1)
|
||||||
signal.Notify(c, sig)
|
|
||||||
return c
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRefresh(newWrap func() Wrap, ch <-chan os.Signal) *Refresh {
|
func NewRefreshWith(ctx context.Context, newWrap func() Wrap, ch <-chan os.Signal) *Refresh {
|
||||||
ctx, can := context.WithCancel(context.Background())
|
ctx, can := context.WithCancel(ctx)
|
||||||
result := &Refresh{
|
result := &Refresh{
|
||||||
can: can,
|
can: can,
|
||||||
input: newWrap(),
|
input: newWrap(),
|
||||||
@@ -33,6 +41,7 @@ func NewRefresh(newWrap func() Wrap, ch <-chan os.Signal) *Refresh {
|
|||||||
return
|
return
|
||||||
case sig := <-ch:
|
case sig := <-ch:
|
||||||
log.Println("refreshing for", sig)
|
log.Println("refreshing for", sig)
|
||||||
|
result.input.CloseWrap()
|
||||||
result.input = newWrap()
|
result.input = newWrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -40,6 +49,11 @@ func NewRefresh(newWrap func() Wrap, ch <-chan os.Signal) *Refresh {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *Refresh) CloseWrap() raw.Raw {
|
||||||
|
r.can()
|
||||||
|
return r.input.CloseWrap()
|
||||||
|
}
|
||||||
|
|
||||||
func (r *Refresh) Read() []button.Button {
|
func (r *Refresh) Read() []button.Button {
|
||||||
return r.input.Read()
|
return r.input.Read()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package wrap
|
package wrap
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"mayhem-party/src/device/input/button"
|
||||||
"os"
|
"os"
|
||||||
"syscall"
|
"syscall"
|
||||||
"testing"
|
"testing"
|
||||||
@@ -15,7 +17,7 @@ func TestRefresh(t *testing.T) {
|
|||||||
}
|
}
|
||||||
ch := make(chan os.Signal, 1)
|
ch := make(chan os.Signal, 1)
|
||||||
defer close(ch)
|
defer close(ch)
|
||||||
refresh := NewRefresh(generator, ch)
|
refresh := NewRefreshWith(context.Background(), generator, ch)
|
||||||
defer refresh.Close()
|
defer refresh.Close()
|
||||||
|
|
||||||
assertIts := func(t *testing.T, b byte) {
|
assertIts := func(t *testing.T, b byte) {
|
||||||
@@ -42,3 +44,51 @@ func TestRefresh(t *testing.T) {
|
|||||||
assertIts(t, byte('c'))
|
assertIts(t, byte('c'))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRefreshDoesntCloseSources(t *testing.T) {
|
||||||
|
src := &telemetrySrc{}
|
||||||
|
newParsers := 0
|
||||||
|
newParser := func() Wrap {
|
||||||
|
newParsers += 1
|
||||||
|
return button.NewPlaintext(src)
|
||||||
|
}
|
||||||
|
ctx, can := context.WithCancel(context.Background())
|
||||||
|
defer can()
|
||||||
|
refresh := NewRefresh(ctx, newParser)
|
||||||
|
if newParsers != 1 {
|
||||||
|
t.Error(newParsers)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < 5; i++ {
|
||||||
|
refresh.Read()
|
||||||
|
}
|
||||||
|
if want := (telemetrySrc{reads: 5}); *src != want {
|
||||||
|
t.Errorf("%+v", *src)
|
||||||
|
} else if newParsers != 1 {
|
||||||
|
t.Error(newParsers)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < 5; i++ {
|
||||||
|
chSigUsr1 <- syscall.SIGINT
|
||||||
|
}
|
||||||
|
time.Sleep(time.Millisecond * 250)
|
||||||
|
if want := (telemetrySrc{reads: 5}); *src != want {
|
||||||
|
t.Errorf("want %+v, got %+v", want, *src)
|
||||||
|
} else if newParsers != 6 {
|
||||||
|
t.Error(newParsers)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type telemetrySrc struct {
|
||||||
|
closes int
|
||||||
|
reads int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (src *telemetrySrc) Close() {
|
||||||
|
src.closes += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (src *telemetrySrc) Read() []byte {
|
||||||
|
src.reads += 1
|
||||||
|
return []byte("foo")
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package wrap
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"mayhem-party/src/device/input/button"
|
"mayhem-party/src/device/input/button"
|
||||||
|
"mayhem-party/src/device/input/raw"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/go-yaml/yaml"
|
"github.com/go-yaml/yaml"
|
||||||
@@ -38,6 +39,10 @@ func NewRemap(input Wrap, m map[byte]byte) Remap {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (re Remap) CloseWrap() raw.Raw {
|
||||||
|
return re.input.CloseWrap()
|
||||||
|
}
|
||||||
|
|
||||||
func (re Remap) Close() {
|
func (re Remap) Close() {
|
||||||
re.input.Close()
|
re.input.Close()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,36 +3,42 @@ package wrap
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"mayhem-party/src/device/input/button"
|
"mayhem-party/src/device/input/button"
|
||||||
|
"mayhem-party/src/device/input/raw"
|
||||||
"os"
|
"os"
|
||||||
"syscall"
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
FlagBuffered = os.Getenv("WRAP_BUFFERED") == "true"
|
||||||
|
FlagRemapFile = os.Getenv("WRAP_REMAP_FILE")
|
||||||
|
FlagRefreshOnSigUsr1 = os.Getenv("WRAP_REFRESH_ON_SIGUSR1") == "true"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Wrap interface {
|
type Wrap interface {
|
||||||
Read() []button.Button
|
Read() []button.Button
|
||||||
Close()
|
Close()
|
||||||
|
CloseWrap() raw.Raw
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(ctx context.Context, srcFunc func() button.Parser) Wrap {
|
func New(ctx context.Context, srcFunc func() button.Parser) Wrap {
|
||||||
maker := func() Wrap {
|
maker := func() Wrap {
|
||||||
return srcFunc()
|
return srcFunc()
|
||||||
}
|
}
|
||||||
if os.Getenv("WRAP_BUFFERED") == "true" {
|
if FlagBuffered {
|
||||||
oldMaker := maker
|
oldMaker := maker
|
||||||
maker = func() Wrap {
|
maker = func() Wrap {
|
||||||
return NewBuffered(ctx, oldMaker())
|
return NewBuffered(ctx, oldMaker())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if p := os.Getenv("WRAP_REMAP_FILE"); p != "" {
|
if FlagRemapFile != "" {
|
||||||
oldMaker := maker
|
oldMaker := maker
|
||||||
maker = func() Wrap {
|
maker = func() Wrap {
|
||||||
return NewRemapFromFile(oldMaker(), p)
|
return NewRemapFromFile(oldMaker(), FlagRemapFile)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if os.Getenv("WRAP_REFRESH_ON_SIGUSR1") != "" {
|
if FlagRefreshOnSigUsr1 {
|
||||||
oldMaker := maker
|
oldMaker := maker
|
||||||
c := NewRefreshCh(syscall.SIGUSR1)
|
|
||||||
maker = func() Wrap {
|
maker = func() Wrap {
|
||||||
return NewRefresh(oldMaker, c)
|
return NewRefresh(ctx, oldMaker)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return maker()
|
return maker()
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package wrap
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"mayhem-party/src/device/input/button"
|
"mayhem-party/src/device/input/button"
|
||||||
|
"mayhem-party/src/device/input/raw"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -10,11 +11,11 @@ func TestWrap(t *testing.T) {
|
|||||||
var _ Wrap = &Refresh{}
|
var _ Wrap = &Refresh{}
|
||||||
var _ Wrap = &Buffered{}
|
var _ Wrap = &Buffered{}
|
||||||
var _ Wrap = &Remap{}
|
var _ Wrap = &Remap{}
|
||||||
var _ Wrap = Protocol{}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type dummyParser button.Button
|
type dummyParser button.Button
|
||||||
|
|
||||||
|
func (d dummyParser) CloseWrap() raw.Raw { return nil }
|
||||||
func (d dummyParser) Close() {}
|
func (d dummyParser) Close() {}
|
||||||
func (d dummyParser) Read() []button.Button {
|
func (d dummyParser) Read() []button.Button {
|
||||||
return []button.Button{button.Button(d)}
|
return []button.Button{button.Button(d)}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ func Main(ctx context.Context) error {
|
|||||||
defer reader.Close()
|
defer reader.Close()
|
||||||
|
|
||||||
interval := time.Millisecond * 50
|
interval := time.Millisecond * 50
|
||||||
if intervalS, ok := os.LookupEnv("MAIN_INTERVAL_DURATION"); !ok {
|
if intervalS := os.Getenv("MAIN_INTERVAL_DURATION"); intervalS != "" {
|
||||||
} else if v, err := time.ParseDuration(intervalS); err != nil {
|
} else if v, err := time.ParseDuration(intervalS); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
16
todo.yaml
16
todo.yaml
@@ -1,6 +1,6 @@
|
|||||||
todo:
|
todo:
|
||||||
- change from 'a','b','c' from rust to just 11,21,31,41 so playerName is known implicitly
|
- v01cfg includes messages to send per client and exposes http server for it
|
||||||
- lag via UDP formatted inputs as space-delimited TS PID buttonIdx buttonIdx buttonIdx
|
- send clients messages to display
|
||||||
- input.MayhemParty as a logical wrapper from mod10 but then gotta translate back
|
- input.MayhemParty as a logical wrapper from mod10 but then gotta translate back
|
||||||
to char for keyboard things somewhere; space delimited?
|
to char for keyboard things somewhere; space delimited?
|
||||||
- todo: rusty configs have "name" for each client
|
- todo: rusty configs have "name" for each client
|
||||||
@@ -42,3 +42,15 @@ done:
|
|||||||
- todo: input.MayhemParty as a logical wrapper from %10 but then gotta translate back
|
- todo: input.MayhemParty as a logical wrapper from %10 but then gotta translate back
|
||||||
to char for keyboard things somewhere; space delimited?
|
to char for keyboard things somewhere; space delimited?
|
||||||
ts: Fri Mar 24 21:16:39 MDT 2023
|
ts: Fri Mar 24 21:16:39 MDT 2023
|
||||||
|
- todo: change from 'a','b','c' from rust to just 11,21,31,41 so playerName is known
|
||||||
|
implicitly
|
||||||
|
ts: Sat Mar 25 00:06:21 MDT 2023
|
||||||
|
- todo: lag via UDP formatted inputs as space-delimited TS PID buttonIdx buttonIdx
|
||||||
|
buttonIdx
|
||||||
|
ts: Sat Mar 25 00:13:19 MDT 2023
|
||||||
|
- todo: map keys triggered by user to player idx and their keys
|
||||||
|
ts: Sat Mar 25 00:44:19 MDT 2023
|
||||||
|
- todo: use button.V01Cfg; map keys triggered by user to player idx and their keys
|
||||||
|
ts: Sat Mar 25 09:12:43 MDT 2023
|
||||||
|
- todo: v01cfg includes messages to send per client and exposes tcp server for it
|
||||||
|
ts: Sat Mar 25 10:09:06 MDT 2023
|
||||||
|
|||||||
Reference in New Issue
Block a user