Skip to content

Commit

Permalink
Updated readme
Browse files Browse the repository at this point in the history
  • Loading branch information
James Rutherford / creativenucleus committed Nov 3, 2023
2 parents 5acbf22 + e24bd53 commit c59d83d
Show file tree
Hide file tree
Showing 229 changed files with 2,656 additions and 665 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/_bytejammer-data/*
/external/*
/work/*
/*.exe
/*.exe~
bytejammer.code-workspace
42 changes: 26 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ Features should currently be considered experimental, and liable to change, poss

## Larger Known Issues

Microsoft Windows Defender currently flags this program as a Trojan (`Bearfoos.A!ml`) when run in server mode. I believe this is a false positive - there are quite a few examples of [other people complaining around this on the web](https://www.google.com/search?q="Bearfoos.A!ml") and it seems to be [just Windows Defender](https://www.virustotal.com/gui/file/a479a1edb273dafec1920fb7210863d48ce9bd2a0f394ad5ff1cd0478468a18c?nocache=1). I'll see if I can get it unflagged. You're welcome to check the source code and build it yourself (if you want total peace of mind, you'll also need to rebuild the TIC-80 binary and replace that in the `embed` folder).

Some files that are meant to be temporary don't get removed.

The Identity mechanism is incomplete. It's likely that this will be updated.
Expand All @@ -27,7 +29,9 @@ This is intended as a standalone TIC-80 launcher that can be used to coordinate

Cross-compatible on Windows, Mac, and Linux.

## Runnning
## Running

To get the latest version, visit the [GitHub repo latest releases page](https://github.com/creativenucleus/bytejammer/releases/).

Make sure you place it in a folder of its own. It will create a subfolder to hold some temporary files.

Expand All @@ -39,19 +43,20 @@ Default (no arguments) mode will launch into jukebox mode, playing random Byteja
It can be provided with a JSON file playlist (from remote and local) or .zip file.

Applications:
- To project onto a wall at parties to preach the good TIC and Bytejam words.
- To play at events like the recent Unesco one.
- For DJ visuals.
- Run by a server as a placeholder player for Bytejams.
- For people to just enjoy in their own homes.
- An ad-hoc retro-style kiosk/ad runner.
- For a party to showcase all the entries in a competition.

- To project onto a wall at parties to preach the good TIC and Bytejam words.
- To play at events like the recent Unesco one.
- For DJ visuals.
- Run by a server as a placeholder player for Bytejams.
- For people to just enjoy in their own homes.
- An ad-hoc retro-style kiosk/ad runner.
- For a party to showcase all the entries in a competition.

`bytejammer.exe` (default - Livecode DemoZoo ByteJam playlist)

`bytejammer.exe jukebox --playlist .\playlist\trains.json` (play a JSON playlist)

`bytejammer.exe jukebox --playlist .\playlist\nanogems-test-selection.zip` (play files from a .zip)
`bytejammer.exe jukebox --playlist .\playlist\example-jams-jtruk.zip` (play files from a .zip)

### Server Mode

Expand All @@ -62,8 +67,9 @@ Clients can be wired to display TICs, which will spawn as appropriate.
The jukebox is also a client, just run by a robot.
The panel will allow the server operator to snapshot code, switch the links between clients and display TICs, and push code to clients.
If input+output can work on a client, also maybe useful for education:
- The server can send some code to all clients
- After an exercise, pull each in turn to display for showcase.

- The server can send some code to all clients
- After an exercise, pull each in turn to display for showcase.

`bytejammer.exe server` // (Optional: can specify `--localport 4444`)

Expand Down Expand Up @@ -98,15 +104,19 @@ You can connect a client-jukebox to a remote server, as you would a regular clie

- Remove machine / remove client from link table when closed.
- Better gatekeeping of client 'lobby'.
- Improve web panels.
- Reintroduce NuSan Launcher.
- Authentication by key.
- Clean close/open.
- Messaging feature.
- Obfuscate session
- Rationalise capitalisation/skewer of AJAX/WS data.
- Normalise filepath final slash throughout code (pick one!)

### Ideas
### Ideas / Later

- TIC-80 version management.
- LCDZ json builds. Add keyword tags?
- Wrap WebSocket/Mutex in a class.
- Pure proxy mode (to sit on a server and facilitate connections between clients without fixed ips).
- Multiple hosted ports per server?
Expand All @@ -120,7 +130,7 @@ You can connect a client-jukebox to a remote server, as you would a regular clie
- Act as a relay (hub), fan out one code to many, or converge / round robin to one display? (applications?)
- Is possible: Auto layout of OBS Studio, or a layer in between?
- Code posting to a web client via WebSocket?
- Support other fantasy consoles (Bazematic? MicroW8? Pico8?) / spec out.
- Support other fantasy consoles (Bonzomatic? Bazematic? MicroW8? Pico8?) / spec out.

## Alternatives

Expand All @@ -146,6 +156,6 @@ This project operates with the following sub-licenses:

## Links and References

[Listing / DemoZoo](https://demozoo.org/productions/330626/)
[Listing / Pouët](https://pouet.net/prod.php?which=95232)
[Overview Video / YouTube](https://youtube.com/watch?v=erhyvrGxwZY)
- [Listing / DemoZoo](https://demozoo.org/productions/330626/)
- [Listing / Pouët](https://pouet.net/prod.php?which=95232)
- [Overview Video / YouTube](https://youtube.com/watch?v=erhyvrGxwZY)
12 changes: 11 additions & 1 deletion api-json.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,17 @@ import (

func apiOutErr(w http.ResponseWriter, err error, statusCode int) {
w.Header().Set("Content-Type", "application/json")
http.Error(w, err.Error(), statusCode)
w.WriteHeader(statusCode)

type errJson struct {
Error string `json:"error"`
}

out := errJson{
Error: err.Error(),
}

json.NewEncoder(w).Encode(out)
}

func apiOutResponse(w http.ResponseWriter, data interface{}, statusCode int) {
Expand Down
6 changes: 4 additions & 2 deletions client-jukebox.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ func startClientJukebox(host string, port int, playlist *Playlist) error {
return err
}

ws, err := NewWebSocketClient(host, port)
ws, err := NewWebSocketLink(host, port, "/ws-bytejam")
if err != nil {
return err
}
Expand All @@ -22,7 +22,9 @@ func startClientJukebox(host string, port int, playlist *Playlist) error {
if ok {
switch msg.Type {
case "tic-state":
err := ws.sendCode(msg.TicState)
// #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)
Expand Down
19 changes: 10 additions & 9 deletions client-panel.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"encoding/json"
"errors"
"fmt"
"io/fs"
"log"
"net/http"
"strconv"
Expand All @@ -16,6 +17,10 @@ import (
"github.com/creativenucleus/bytejammer/embed"
)

// ClientPanel is the web interface for the client to manage their connection and the port should be private to them.
// It handles identity creation, and launching of a connection to the host.
// It does not handle the connection with the host itself.

const (
fileCheckPeriod = 3 * time.Second
)
Expand All @@ -36,12 +41,11 @@ func startClientPanel(port int) error {
ReadHeaderTimeout: 3 * time.Second,
}

fs := http.FileServer(http.Dir("./web-static"))
http.Handle("/static/", http.StripPrefix("/static/", fs))

http.HandleFunc("/favicon.ico", func(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, "/web-static/favicon/favicon.ico")
})
subFs, err := fs.Sub(embed.WebStaticAssets, "web-static")
if err != nil {
return err
}
http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.FS(subFs))))

fmt.Printf("In a web browser, go to http://localhost:%d/%s\n", port, session)

Expand Down Expand Up @@ -125,7 +129,6 @@ func (cp *ClientPanel) webClientApiJoinServerJSON(w http.ResponseWriter, r *http
return
}

fmt.Println(req.IdentityId)
identity, err := getIdentity(req.IdentityId)
if err != nil {
apiOutErr(w, err, http.StatusBadRequest)
Expand Down Expand Up @@ -179,8 +182,6 @@ func (cp *ClientPanel) wsRead() {
}

switch msg.Type {
case "reset-clients":
// s.resetAllClients()
default:
log.Printf("Message not understood: %s\n", msg.Type)
}
Expand Down
57 changes: 47 additions & 10 deletions client-ws.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,13 @@ import (

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

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

type ClientServerStatus struct {
Expand All @@ -21,7 +24,17 @@ type ClientServerStatus struct {

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

cws.basepath = filepath.Clean(fmt.Sprintf("%sclient-data/%s", config.WORK_DIR, util.GetSlugFromTime(time.Now())))
// chLog <- fmt.Sprintf("Creating directory: %s", cws.basepath)
err := util.EnsurePathExists(cws.basepath, os.ModePerm)
if err != nil {
return err
}

// Keep running until we make a connection
for {
// #TODO: This is not the right construction
Expand Down Expand Up @@ -54,8 +67,8 @@ func startClientServerConn(host string, port int, identity *Identity, chServerSt
}
}

func clientOpenConnection(host string, port int) (*SenderWebSocket, error) {
ws, err := NewWebSocketClient(host, port)
func clientOpenConnection(host string, port int) (*WebSocketLink, error) {
ws, err := NewWebSocketLink(host, port, "/ws-bytejam")
if err != nil {
return nil, err
}
Expand All @@ -78,15 +91,36 @@ func (cws *ClientWS) clientWsReader(tic *machines.Tic) error {
}

switch msg.Type {
case "challenge-request":
cws.handleChallengeRequest(msg.ChallengeRequest.Challenge)

case "tic-state":
tic.WriteImportCode(msg.TicState)
}
}
}

func (cws *ClientWS) handleChallengeRequest(challenge string) {
cws.chMsg <- Msg{Type: "challenge-response", ChallengeResponse: DataChallengeResponse{Challenge: challenge + " ~ response"}}
}

// #TODO: fatalErr
func (cws *ClientWS) clientWsWriter(tic *machines.Tic, identity *Identity) {
err := cws.ws.sendIdentity(identity)
// Send Identity...
publicKeyRaw, err := identity.Crypto.publicKeyToRaw()
if err != nil {
log.Fatal(err)
}

msg := Msg{
Type: "identity",
Identity: DataIdentity{
Uuid: identity.Uuid.String(),
DisplayName: identity.DisplayName,
PublicKey: publicKeyRaw,
},
}
err = cws.ws.sendData(&msg)
if err != nil {
log.Fatal(err)
}
Expand All @@ -99,6 +133,8 @@ func (cws *ClientWS) clientWsWriter(tic *machines.Tic, identity *Identity) {
var lastTicState *machines.TicState
for {
select {
case msg := <-cws.chMsg:
cws.ws.sendData(msg)
// case <-done:
// return
case <-fileCheckTicker.C:
Expand All @@ -116,14 +152,16 @@ func (cws *ClientWS) clientWsWriter(tic *machines.Tic, identity *Identity) {
}

if ticState.IsRunning {
err := saveCode(ticState.Code)
err := cws.saveCode(ticState.Code)
if err != nil {
log.Fatal(err)
break
}
}

err = cws.ws.sendCode(*ticState)
// #TODO: line endings for data? UTF-8?
msg := Msg{Type: "tic-state", TicState: *ticState}
err = cws.ws.sendData(msg)
if err != nil {
log.Fatal(err)
break
Expand Down Expand Up @@ -160,8 +198,7 @@ func readFile(filename string) ([]byte, error) {
return data, nil
}

func saveCode(code []byte) error {
now := time.Now()
path := filepath.Clean(fmt.Sprintf("%scode-%s", config.WORK_DIR, now.Format("20060102-150405")))
func (cws *ClientWS) saveCode(code []byte) error {
path := filepath.Clean(fmt.Sprintf("%s/code-%s.lua", cws.basepath, util.GetSlugFromTime(time.Now())))
return os.WriteFile(path, code, 0644)
}
2 changes: 1 addition & 1 deletion config/config.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
package config

const (
WORK_DIR = "./work/"
WORK_DIR = "./_bytejammer-data/"
)
Loading

0 comments on commit c59d83d

Please sign in to comment.