intergrated laplace to remotegameplay

This commit is contained in:
2021-07-24 23:21:22 +04:00
parent 8e33c63dbf
commit 90b3a076e0
23 changed files with 1836 additions and 189 deletions

89
core/barrier-kvm.go Normal file
View File

@@ -0,0 +1,89 @@
// Package core
// We will implement the popular open source project barrier kvm
// To ensure we can connect clients keyboard and mouse to the server
// while ScreenShare is taking place
package core
import (
"laplace/config"
"os/exec"
)
// Barrier It's preferred that the IP address used is a IPV6 address
type Barrier struct {
NodeName string
IPAddress string
Mode string
Process *exec.Cmd
}
// CreateBarrierSession Command to run "barrier.barrierc --debug INFO -f 192.168.0.175"
func (b *Barrier)CreateBarrierSession() error {
//Checks if barrier client exists
if err := DetectBarrier(); err != nil {
return err
}
//Get username from config file
configResp, err := config.ConfigInit()
if err != nil {
return err
}
cmd := exec.Command("sudo","-u",configResp.SystemUsername,"barrier.barrierc","-f","--debug", "DEBUG" ,"--log", "/tmp/barrier.log",b.IPAddress)
// USE THE FOLLOWING TO DEBUG
//cmdReader, err := cmd.StdoutPipe()
//if err != nil {
// fmt.Fprintln(os.Stderr, "Error creating StdoutPipe for Cmd", err)
// return
//}
//
//// the following is used to print output of the command
//// as it makes progress...
//scanner := bufio.NewScanner(cmdReader)
//go func() {
// for scanner.Scan() {
// fmt.Printf("%s\n", scanner.Text())
// //
// // TODO:
// // send output to server
// }
//}()
//
//if err := cmd.Start(); err != nil {
// return err
//}
if err := cmd.Start(); err != nil {
return err
}
println(cmd.Path)
// Saves the state of the command in the struct
b.Process = cmd
return nil
}
// DeleteBarrierSession Deletes barrier client session running
func (b *Barrier)DeleteBarrierSession() error {
// Halts the process
cmd := exec.Command("pkill" ,"barrierc")
if err := cmd.Run(); err != nil {
return err
}
return nil
}
// DetectBarrier This function ensures that the server has barrier client installed
func DetectBarrier() error {
_, err := exec.LookPath("barrier.barrierc")
if err != nil {
return err
}
return nil
}

31
core/barrier-kvm_test.go Normal file
View File

@@ -0,0 +1,31 @@
package core
import (
"testing"
"time"
)
// To run this test ensure you have barrier
// installed
func TestDetectBarrier(t *testing.T) {
err := DetectBarrier()
if err != nil {
t.Error()
}
}
func TestBarrier_CreateBarrierSession_DeleteBarrierSession(t *testing.T) {
var testIP Barrier
// Change this with the test machine controlling the
// keyboard and mouse
testIP.IPAddress = "192.168.0.175"
if err := testIP.CreateBarrierSession(); err != nil {
t.Error()
}
// Create delay
time.Sleep(5 * time.Second)
if err := testIP.DeleteBarrierSession(); err != nil {
t.Error()
}
}

497
core/names-generator.go Normal file
View File

@@ -0,0 +1,497 @@
package core
import (
"fmt"
"math/rand"
)
var (
left = [...]string{
"admiring",
"adoring",
"affectionate",
"agitated",
"amazing",
"angry",
"awesome",
"beautiful",
"blissful",
"bold",
"boring",
"brave",
"busy",
"charming",
"clever",
"cool",
"compassionate",
"competent",
"condescending",
"confident",
"cranky",
"crazy",
"dazzling",
"determined",
"distracted",
"dreamy",
"eager",
"ecstatic",
"elastic",
"elated",
"elegant",
"eloquent",
"epic",
"exciting",
"fervent",
"festive",
"flamboyant",
"focused",
"friendly",
"frosty",
"funny",
"gallant",
"gifted",
"goofy",
"gracious",
"great",
"happy",
"hardcore",
"heuristic",
"hopeful",
"hungry",
"infallible",
"inspiring",
"interesting",
"intelligent",
"jolly",
"jovial",
"keen",
"kind",
"laughing",
"loving",
"lucid",
"magical",
"mystifying",
"modest",
"musing",
"naughty",
"nervous",
"nice",
"nifty",
"nostalgic",
"objective",
"optimistic",
"peaceful",
"pedantic",
"pensive",
"practical",
"priceless",
"quirky",
"quizzical",
"recursing",
"relaxed",
"reverent",
"romantic",
"sad",
"serene",
"sharp",
"silly",
"sleepy",
"stoic",
"strange",
"stupefied",
"suspicious",
"sweet",
"tender",
"thirsty",
"trusting",
"unruffled",
"upbeat",
"vibrant",
"vigilant",
"vigorous",
"wizardly",
"wonderful",
"xenodochial",
"youthful",
"zealous",
"zen",
}
// Docker, starting from 0.7.x, generates names from notable scientists and hackers.
// Please, for any amazing man that you add to the list, consider adding an equally amazing woman to it, and vice versa.
right = [...]string{
"aardvark",
"abyssinian",
"affenpinscher",
"akbash",
"akita",
"albatross",
"alligator",
"alpaca",
"angelfish",
"ant",
"anteater",
"antelope",
"ape",
"armadillo",
"ass",
"avocet",
"axolotl",
"baboon",
"badger",
"balinese",
"bandicoot",
"barb",
"barnacle",
"barracuda",
"bat",
"beagle",
"bear",
"beaver",
"bee",
"beetle",
"binturong",
"bird",
"birman",
"bison",
"bloodhound",
"boar",
"bobcat",
"bombay",
"bongo",
"bonobo",
"booby",
"budgerigar",
"buffalo",
"bulldog",
"bullfrog",
"burmese",
"butterfly",
"caiman",
"camel",
"capybara",
"caracal",
"caribou",
"cassowary",
"cat",
"caterpillar",
"catfish",
"cattle",
"centipede",
"chameleon",
"chamois",
"cheetah",
"chicken",
"chihuahua",
"chimpanzee",
"chinchilla",
"chinook",
"chipmunk",
"chough",
"cichlid",
"clam",
"coati",
"cobra",
"cockroach",
"cod",
"collie",
"coral",
"cormorant",
"cougar",
"cow",
"coyote",
"crab",
"crane",
"crocodile",
"crow",
"curlew",
"cuscus",
"cuttlefish",
"dachshund",
"dalmatian",
"deer",
"dhole",
"dingo",
"dinosaur",
"discus",
"dodo",
"dog",
"dogfish",
"dolphin",
"donkey",
"dormouse",
"dotterel",
"dove",
"dragonfly",
"drever",
"duck",
"dugong",
"dunker",
"dunlin",
"eagle",
"earwig",
"echidna",
"eel",
"eland",
"elephant",
"elk",
"emu",
"falcon",
"ferret",
"finch",
"fish",
"flamingo",
"flounder",
"fly",
"fossa",
"fox",
"frigatebird",
"frog",
"galago",
"gar",
"gaur",
"gazelle",
"gecko",
"gerbil",
"gharial",
"gibbon",
"giraffe",
"gnat",
"gnu",
"goat",
"goldfinch",
"goldfish",
"goose",
"gopher",
"gorilla",
"goshawk",
"grasshopper",
"greyhound",
"grouse",
"guanaco",
"gull",
"guppy",
"hamster",
"hare",
"harrier",
"havanese",
"hawk",
"hedgehog",
"heron",
"herring",
"himalayan",
"hippopotamus",
"hornet",
"horse",
"human",
"hummingbird",
"hyena",
"ibis",
"iguana",
"impala",
"indri",
"insect",
"jackal",
"jaguar",
"javanese",
"jay",
"jellyfish",
"kakapo",
"kangaroo",
"kingfisher",
"kiwi",
"koala",
"kouprey",
"kudu",
"labradoodle",
"ladybird",
"lapwing",
"lark",
"lemming",
"lemur",
"leopard",
"liger",
"lion",
"lionfish",
"lizard",
"llama",
"lobster",
"locust",
"loris",
"louse",
"lynx",
"lyrebird",
"macaw",
"magpie",
"mallard",
"maltese",
"manatee",
"mandrill",
"markhor",
"marten",
"mastiff",
"mayfly",
"meerkat",
"millipede",
"mink",
"mole",
"molly",
"mongoose",
"mongrel",
"monkey",
"moorhen",
"moose",
"mosquito",
"moth",
"mouse",
"mule",
"narwhal",
"neanderthal",
"newfoundland",
"newt",
"nightingale",
"numbat",
"ocelot",
"octopus",
"okapi",
"olm",
"opossum",
"orangutan",
"oryx",
"ostrich",
"otter",
"owl",
"ox",
"oyster",
"pademelon",
"panther",
"parrot",
"partridge",
"peacock",
"peafowl",
"pekingese",
"pelican",
"penguin",
"persian",
"pheasant",
"pig",
"pigeon",
"pika",
"pike",
"piranha",
"platypus",
"pointer",
"pony",
"poodle",
"porcupine",
"porpoise",
"possum",
"prawn",
"puffin",
"pug",
"puma",
"quail",
"quelea",
"quetzal",
"quokka",
"quoll",
"rabbit",
"raccoon",
"ragdoll",
"rail",
"ram",
"rat",
"rattlesnake",
"raven",
"reindeer",
"rhinoceros",
"robin",
"rook",
"rottweiler",
"ruff",
"salamander",
"salmon",
"sandpiper",
"saola",
"sardine",
"scorpion",
"seahorse",
"seal",
"serval",
"shark",
"sheep",
"shrew",
"shrimp",
"siamese",
"siberian",
"skunk",
"sloth",
"snail",
"snake",
"snowshoe",
"somali",
"sparrow",
"spider",
"sponge",
"squid",
"squirrel",
"starfish",
"starling",
"stingray",
"stinkbug",
"stoat",
"stork",
"swallow",
"swan",
"tang",
"tapir",
"tarsier",
"termite",
"tetra",
"tiffany",
"tiger",
"toad",
"tortoise",
"toucan",
"tropicbird",
"trout",
"tuatara",
"turkey",
"turtle",
"uakari",
"uguisu",
"umbrellabird",
"viper",
"vulture",
"wallaby",
"walrus",
"warthog",
"wasp",
"weasel",
"whale",
"whippet",
"wildebeest",
"wolf",
"wolverine",
"wombat",
"woodcock",
"woodlouse",
"woodpecker",
"worm",
"wrasse",
"wren",
"yak",
"zebra",
"zebu",
"zonkey",
"zorse",
}
)
func GetRandomName(retry int) string {
name := fmt.Sprintf("%s_%s_%s", left[rand.Intn(len(left))], left[rand.Intn(len(left))], right[rand.Intn(len(right))])
if retry > 0 {
name = fmt.Sprintf("%s_%d", name, rand.Intn(10))
}
return name
}

140
core/room.go Normal file
View File

@@ -0,0 +1,140 @@
package core
import (
"encoding/json"
"fmt"
"github.com/gorilla/websocket"
"io/ioutil"
"laplace/config"
"os"
)
type Room struct {
ID string `json:"id"`
Sessions map[string]*StreamSession
CallerConn *websocket.Conn
BarrierSession *Barrier
}
type StreamSession struct {
ID string
Offer string
Answer string
CallerIceCandidates []string
CalleeIceCandidates []string
CallerConn *websocket.Conn
CalleeConn *websocket.Conn
}
var roomMap = make(map[string]*Room)
func GetRoom(id string) *Room {
return roomMap[id]
}
func NewRoom(callerConn *websocket.Conn) *Room {
room := Room{
ID: newRoomID(),
Sessions: make(map[string]*StreamSession),
CallerConn: callerConn,
}
roomMap[room.ID] = &room
err := room.writeToFile()
if err != nil {
println(err)
}
return &room
}
func newRoomID() string {
id := GetRandomName(0)
for GetRoom(id) != nil {
id = GetRandomName(0)
}
return id
}
func RemoveRoom(id string) {
roomMap[id] = nil
// Writes nil to the file
roomMap[id].writeToFile()
}
func (room *Room) GetSession(id string) *StreamSession {
return room.Sessions[id]
}
func (room *Room) NewSession(calleeConn *websocket.Conn) *StreamSession {
session := StreamSession{
ID: room.newSessionID(),
CallerIceCandidates: []string{},
CalleeIceCandidates: []string{},
CallerConn: room.CallerConn,
CalleeConn: calleeConn,
}
room.Sessions[session.ID] = &session
return &session
}
func (room *Room) newSessionID() string {
id := fmt.Sprintf("%s$%s", room.ID, GetRandomName(0))
for GetRoom(id) != nil {
id = fmt.Sprintf("%s$%s", room.ID, GetRandomName(0))
}
return id
}
// Write to rooms.json file
func (room *Room)writeToFile() error {
file, err := json.MarshalIndent(room, "", " ")
if err != nil {
return err
}
// Get Path from config
config, err := config.ConfigInit()
if err != nil {
return err
}
// Write room struct to json file
err = ioutil.WriteFile(config.Rooms, file, 0644)
if err != nil {
return err
}
return nil
}
// ReadRoomsFile Reads rooms file and return struct room id
func ReadRoomsFile() (*Room, error) {
config, err := config.ConfigInit()
if err != nil {
return nil,err
}
jsonFile, err := os.Open(config.Rooms)
// if we os.Open returns an error then handle it
if err != nil {
return nil,err
}
// defer the closing of our jsonFile so that we can parse it later on
defer jsonFile.Close()
// read our opened xmlFile as a byte array.
byteValue, _ := ioutil.ReadAll(jsonFile)
// we initialize our Users array
var rooms *Room
// we unmarshal our byteArray which contains our
// jsonFile's content into 'users' which we defined above
json.Unmarshal(byteValue, &rooms)
return rooms, nil
}

197
core/signal.go Normal file
View File

@@ -0,0 +1,197 @@
package core
import (
"fmt"
"github.com/gorilla/websocket"
"laplace/config"
"log"
"net/http"
"time"
)
type WSMessage struct {
SessionID string
Type string
Value string
}
var upgrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
}
func sendHeartBeatWS(ticker *time.Ticker, conn *websocket.Conn, quit chan struct{}) {
for {
select {
case <- ticker.C:
_ = conn.WriteJSON(WSMessage{
Type: "beat",
})
case <- quit:
log.Println("heartbeat stopped")
return
}
}
}
func GetHttp() *http.ServeMux {
server := http.NewServeMux()
server.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("files/static"))))
server.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, "files/main.html")
})
//Get hostname of the current machine
server.HandleFunc("/hostname", func(w http.ResponseWriter, r *http.Request) {
// Read hostname from config file
configResp, err := config.ConfigInit()
if err != nil {
print(err)
return
}
fmt.Fprintf(w, configResp.BarrierHostName)
})
server.HandleFunc("/ws_serve", func(writer http.ResponseWriter, request *http.Request) {
conn, _ := upgrader.Upgrade(writer, request, nil)
room := NewRoom(conn)
if err := conn.WriteJSON(WSMessage{
SessionID: "",
Type: "newRoom",
Value: room.ID,
}); err != nil {
log.Println("newSessionWriteJsonError.", err)
return
}
go func(r *Room) {
ticker := time.NewTicker(10 * time.Second)
quit := make(chan struct{})
defer func() {
ticker.Stop()
_ = room.CallerConn.Close()
close(quit)
RemoveRoom(r.ID)
// Close the barrier session
if r.BarrierSession != nil {
r.BarrierSession.DeleteBarrierSession()
}
for sID, s := range r.Sessions {
_ = s.CalleeConn.WriteJSON(WSMessage{
Type: "roomClosed",
SessionID: sID,
})
}
}()
go sendHeartBeatWS(ticker, conn, quit)
//noinspection ALL
defer room.CallerConn.Close()
for {
var msg WSMessage
if err := room.CallerConn.ReadJSON(&msg); err != nil {
log.Println("websocketError.", err)
return
}
//log.Println(msg)
s := room.GetSession(msg.SessionID)
if s == nil {
log.Println("session nil.", msg.SessionID)
}
if msg.Type == "addCallerIceCandidate" {
s.CallerIceCandidates = append(s.CallerIceCandidates, msg.Value)
} else if msg.Type == "gotOffer" {
s.Offer = msg.Value
}
if err := s.CalleeConn.WriteJSON(msg); err != nil {
log.Println("serveEchoWriteJsonError.", err)
}
}
}(room)
})
server.HandleFunc("/ws_connect", func(writer http.ResponseWriter, request *http.Request) {
conn, _ := upgrader.Upgrade(writer, request, nil)
ids, ok := request.URL.Query()["id"]
if !ok || ids[0] == "" {
return
}
room := GetRoom(ids[0])
ip, ok := request.URL.Query()["barrierip"]
if ok && ip[0] != "" {
//Declaring struct
var barriersession Barrier
barriersession.IPAddress = ip[0]
room.BarrierSession = &barriersession
// Creates a barrier session
err := room.BarrierSession.CreateBarrierSession()
if err != nil {
return
}
}
if room == nil {
_ = conn.WriteJSON(WSMessage{
Type: "roomNotFound",
})
return
}
session := room.NewSession(conn)
if err := room.CallerConn.WriteJSON(WSMessage{
SessionID: session.ID,
Type: "newSession",
Value: session.ID,
}); err != nil {
log.Println("callerWriteJsonError.", err)
return
}
if err := conn.WriteJSON(WSMessage{
SessionID: session.ID,
Type: "newSession",
Value: session.ID,
}); err != nil {
log.Println("calleeWriteJsonError.", err)
return
}
go func(s *StreamSession) {
//noinspection ALL
defer s.CalleeConn.Close()
for {
var msg WSMessage
if err := conn.ReadJSON(&msg); err != nil {
log.Println("websocketError.", err)
return
}
//log.Println(msg)
if msg.SessionID == s.ID {
if msg.Type == "addCalleeIceCandidate" {
s.CalleeIceCandidates = append(s.CalleeIceCandidates, msg.Value)
} else if msg.Type == "gotAnswer" {
s.Answer = msg.Value
}
if err := s.CallerConn.WriteJSON(msg); err != nil {
log.Println("connectEchoWriteJsonError.", err)
}
}
}
}(session)
})
return server
}