Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,25 @@ Make sure you place it in a folder of its own. It will create a subfolder to hol
Default (no arguments) mode will launch into jukebox mode, playing random Bytejams from LCDZ.
It can be provided with a JSON file playlist (from remote and local) or .zip file.

The JSON file format looks like:
```JSON
{
"title": "TIC-80 selected works",
"description": "(with FFT)", // (optional)
"items": [
{
"location": "https://livecode.demozoo.org/shader_file_sources/2023_05_15_byte_jam_monday_night_bytes/nusan.lua",
"author": "Nusan", // (optional)
"description": "FieldFX Byte Jam - 15/05/2023", // (optional)
"playtime": 30 // (optional)
}
]
}
```

A JSON file can be included in a zip file with the name 'index.json'


Applications:

- To project onto a wall at parties to preach the good TIC and Bytejam words.
Expand Down Expand Up @@ -142,6 +161,10 @@ For the previous work ByteJammer builds on, testing, good-will, and support:

Aldroid, Gasman, Lex Bailey, Mantratronic, NesBox, NuSan, PS, Raccoon Violet, Superogue, Totetmatt.

Additional development:

zeno4ever.

Thanks to those whose work features on the [walkthrough video](https://youtube.com/watch?v=erhyvrGxwZY):

Alia, Aldroid, Dave84, Gasman, Lex Bailey, Gigabates, Mantratronic, NuSan, PS, Superogue, Suule, Synesthesia, TôBach.
Expand Down
38 changes: 22 additions & 16 deletions client-jukebox.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
package main

import "log"
import (
"log"
"time"

func startClientJukebox(host string, port int, playlist *Playlist) error {
ch := make(chan Msg)
j, err := NewJukebox(playlist, &ch)
"github.com/creativenucleus/bytejammer/comms"
)

func startClientJukebox(host string, port int, playtime time.Duration, playlist *Playlist) error {
ch := make(chan comms.Msg)
j, err := NewJukebox(playlist, playtime, &ch)
if err != nil {
return err
}
Expand All @@ -17,25 +22,26 @@ func startClientJukebox(host string, port int, playlist *Playlist) error {

go func() {
for {
select {
case msg, ok := <-ch:
if ok {
switch msg.Type {
case "tic-state":
// #TODO: line endings for data? UTF-8?
msg := Msg{Type: "tic-state", TicState: msg.TicState}
err = ws.sendData(msg)
if err != nil {
// #TODO: soften!
log.Fatal(err)
}
msg, ok := <-ch
if ok {
switch msg.Type {
case "tic-state":
// #TODO: line endings for data? UTF-8?
msg := comms.Msg{Type: "tic-state", TicState: msg.TicState}
err = ws.sendData(msg)
if err != nil {
// #TODO: soften!
log.Fatal(err)
}
}
}
}
}()

j.start()

for {
// Removes 100% CPU warning - but this should really be restructured
time.Sleep(10 * time.Second)
}
}
81 changes: 51 additions & 30 deletions client-panel.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"github.com/gorilla/websocket"
"github.com/tyler-sommer/stick"

"github.com/creativenucleus/bytejammer/comms"
"github.com/creativenucleus/bytejammer/embed"
)

Expand All @@ -27,9 +28,10 @@ const (

type ClientPanel struct {
// #TODO: lock down to receiver only
chSendServerStatus chan ClientServerStatus
chSendClientStatus chan comms.DataClientStatus
wsClient *websocket.Conn
wsMutex sync.Mutex
chLog chan string
}

func startClientPanel(port int) error {
Expand All @@ -50,13 +52,23 @@ func startClientPanel(port int) error {
fmt.Printf("In a web browser, go to http://localhost:%d/%s\n", port, session)

cp := ClientPanel{
chSendServerStatus: make(chan ClientServerStatus),
chSendClientStatus: make(chan comms.DataClientStatus),
chLog: make(chan string),
}

go func() {
for {
logMsg := <-cp.chLog
cp.sendLog(logMsg)
}
}()

http.HandleFunc(fmt.Sprintf("/%s", session), cp.webClientIndex)
http.HandleFunc(fmt.Sprintf("/%s/api/identity.json", session), cp.webClientApiIdentityJSON)
http.HandleFunc(fmt.Sprintf("/%s/api/join-server.json", session), cp.webClientApiJoinServerJSON)
http.HandleFunc(fmt.Sprintf("/%s/ws-client", session), cp.wsWebClient())
if err := webServer.ListenAndServe(); err != nil {
err = webServer.ListenAndServe()
if err != nil {
return err
}

Expand Down Expand Up @@ -105,14 +117,16 @@ func (cp *ClientPanel) webClientApiIdentityJSON(w http.ResponseWriter, r *http.R
apiOutResponse(w, nil, http.StatusCreated)

default:
apiOutErr(w, errors.New("Method not allowed"), http.StatusMethodNotAllowed)
apiOutErr(w, fmt.Errorf("method not allowed"), http.StatusMethodNotAllowed)
}
}

func (cp *ClientPanel) webClientApiJoinServerJSON(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case "POST":
cp.chSendServerStatus <- ClientServerStatus{isConnected: false}
cp.chLog <- "Request: Join Server"

cp.chSendClientStatus <- comms.DataClientStatus{IsConnected: false}

// #TODO: Cleaner way to do this?
type reqType struct {
Expand Down Expand Up @@ -141,40 +155,45 @@ func (cp *ClientPanel) webClientApiJoinServerJSON(w http.ResponseWriter, r *http
return
}

err = startClientServerConn(req.Host, port, identity, cp.chSendServerStatus)
err = startClientServerConn(req.Host, port, identity, cp.chSendClientStatus)
if err != nil {
apiOutErr(w, err, http.StatusInternalServerError)
return
}
apiOutResponse(w, nil, http.StatusCreated)

default:
apiOutErr(w, errors.New("Method not allowed"), http.StatusMethodNotAllowed)
apiOutErr(w, errors.New("method not allowed"), http.StatusMethodNotAllowed)
}
}

func (cp *ClientPanel) wsWebClient() func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
var err error
cp.wsClient, err = wsUpgrader.Upgrade(w, r, nil)

comms.WsUpgrade(w, r, func(conn *websocket.Conn) error {
cp.wsClient = conn
defer func() { cp.wsClient = nil }()

go cp.wsRead()
go cp.wsWrite()

// #TODO: handle exit
for {
// Removes 100% CPU warning - but this should really be restructured
time.Sleep(10 * time.Second)
}
})
if err != nil {
log.Print("upgrade:", err)
return
}
defer cp.wsClient.Close()

go cp.wsRead()
go cp.wsWrite()

// #TODO: handle exit
for {
}
}
}

func (cp *ClientPanel) wsRead() {
for {
var msg Msg
var msg comms.Msg
err := cp.wsClient.ReadJSON(&msg)
if err != nil {
log.Println("read:", err)
Expand All @@ -196,23 +215,25 @@ func (cp *ClientPanel) wsWrite() {
}()
*/
for {
select {
// case <-done:
// return
// case <-statusTicker.C:
// fmt.Println("TICKER!")

case status := <-cp.chSendServerStatus:
msg := Msg{Type: "server-status", ServerStatus: status}
err := cp.sendData(&msg)
if err != nil {
// #TODO: relax
log.Fatal(err)
}
status := <-cp.chSendClientStatus
msg := comms.Msg{Type: "client-status", ClientStatus: status}
err := cp.sendData(&msg)
if err != nil {
// #TODO: relax
log.Fatal(err)
}
}
}

func (cp *ClientPanel) sendLog(message string) {
msg := comms.Msg{Type: "log", Log: comms.DataLog{Msg: message}}

err := cp.sendData(&msg)
if err != nil {
log.Println("read:", err)
}
}

func (cp *ClientPanel) sendData(data interface{}) error {
cp.wsMutex.Lock()
defer cp.wsMutex.Unlock()
Expand Down
34 changes: 18 additions & 16 deletions client-ws.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,22 @@ import (
"path/filepath"
"time"

"github.com/creativenucleus/bytejammer/comms"
"github.com/creativenucleus/bytejammer/config"
"github.com/creativenucleus/bytejammer/machines"
"github.com/creativenucleus/bytejammer/util"
)

type ClientWS struct {
ws *WebSocketLink
chMsg chan Msg
chMsg chan comms.Msg
basepath string
}

type ClientServerStatus struct {
isConnected bool
}

func startClientServerConn(host string, port int, identity *Identity, chServerStatus chan ClientServerStatus) error {
chServerStatus <- ClientServerStatus{isConnected: false}
func startClientServerConn(host string, port int, identity *Identity, chServerStatus chan comms.DataClientStatus) error {
chServerStatus <- comms.DataClientStatus{IsConnected: false}
cws := ClientWS{
chMsg: make(chan Msg),
chMsg: make(chan comms.Msg),
}

cws.basepath = filepath.Clean(fmt.Sprintf("%sclient-data/%s", config.WORK_DIR, util.GetSlugFromTime(time.Now())))
Expand All @@ -51,7 +48,7 @@ func startClientServerConn(host string, port int, identity *Identity, chServerSt
break
}
defer cws.ws.Close()
chServerStatus <- ClientServerStatus{isConnected: true}
chServerStatus <- comms.DataClientStatus{IsConnected: true}

m, err := machines.LaunchMachine("TIC-80", true, true, false)
if err != nil {
Expand All @@ -65,6 +62,8 @@ func startClientServerConn(host string, port int, identity *Identity, chServerSt

// Lock #TODO: use a channel to escape
for {
// Removes 100% CPU warning - but this should really be restructured
time.Sleep(10 * time.Second)
}
}

Expand All @@ -79,7 +78,7 @@ func clientOpenConnection(host string, port int) (*WebSocketLink, error) {

func (cws *ClientWS) clientWsReader(tic *machines.Tic, identity *Identity) error {
for {
var msg Msg
var msg comms.Msg
err := cws.ws.conn.ReadJSON(&msg)
if err != nil {
log.Fatal(err)
Expand All @@ -96,7 +95,7 @@ func (cws *ClientWS) clientWsReader(tic *machines.Tic, identity *Identity) error
cws.handleChallengeRequest(msg.ChallengeRequest.Challenge, identity)

case "tic-state":
tic.WriteImportCode(msg.TicState)
tic.WriteImportCode(msg.TicState.State, true)
}
}
}
Expand All @@ -107,13 +106,14 @@ func (cws *ClientWS) handleChallengeRequest(challenge string, identity *Identity
return err
}

fmt.Printf("%x", data)
signed, err := identity.Crypto.Sign(data)
if err != nil {
return err
}

cws.chMsg <- Msg{Type: "challenge-response", ChallengeResponse: DataChallengeResponse{Challenge: fmt.Sprintf("%x", signed)}}
cws.chMsg <- comms.Msg{Type: "challenge-response", ChallengeResponse: comms.DataChallengeResponse{
Challenge: fmt.Sprintf("%x", signed),
}}

return nil
}
Expand All @@ -126,9 +126,9 @@ func (cws *ClientWS) clientWsWriter(tic *machines.Tic, identity *Identity) {
log.Fatal(err)
}

msg := Msg{
msg := comms.Msg{
Type: "identity",
Identity: DataIdentity{
Identity: comms.DataIdentity{
Uuid: identity.Uuid.String(),
DisplayName: identity.DisplayName,
PublicKey: publicKeyRaw,
Expand Down Expand Up @@ -174,7 +174,9 @@ func (cws *ClientWS) clientWsWriter(tic *machines.Tic, identity *Identity) {
}

// #TODO: line endings for data? UTF-8?
msg := Msg{Type: "tic-state", TicState: *ticState}
msg := comms.Msg{Type: "tic-state", TicState: comms.DataTicState{
State: *ticState,
}}
err = cws.ws.sendData(msg)
if err != nil {
log.Fatal(err)
Expand Down
Loading