support for x2x and barrier KVM

This commit is contained in:
2021-10-10 00:09:37 +04:00
parent ce61920ec2
commit 0ed316cb5e
11 changed files with 831 additions and 814 deletions

View File

@@ -24,15 +24,31 @@ https://github.com/Akilan1999/laplace/tree/keyboard_mouse
### Installation required to share keyboard and mouse
We need to ensure that the client has SSH client installed.
We use the popular open repository known as [x2x](https://github.com/dottedmag/x2x).
Note: x2x runs on top of SSH.
Currently, you can either use [x2x](https://github.com/dottedmag/x2x) or [Barrier KVM]()
We need to ensure that the client has SSH client installed or Barrierc.
#### What is x2x?
x2x allows the keyboard, mouse on one X display to be used to control another X
display. It also shares X clipboards between the displays.
Note: x2x runs on top of SSH.
#### What is Barrier kvm?
Barrier is software that mimics the functionality of a KVM switch, which historically would allow you to use a single
keyboard and mouse to control multiple computers by physically turning a dial on the box to switch the machine you're
controlling at any given moment. Barrier does this in software, allowing you to tell it which machine to control by
moving your mouse to the edge of the screen, or by using a keypress to switch focus to a different system.
#### Barrier KVM build status and links to install
|Platform |Build Status|
| --:|:-- |
|Linux |[![Build Status](https://dev.azure.com/debauchee/Barrier/_apis/build/status/debauchee.barrier?branchName=master&jobName=Linux%20Build)](https://dev.azure.com/debauchee/Barrier/_build/latest?definitionId=1&branchName=master)|
|Mac |[![Build Status](https://dev.azure.com/debauchee/Barrier/_apis/build/status/debauchee.barrier?branchName=master&jobName=Mac%20Build)](https://dev.azure.com/debauchee/Barrier/_build/latest?definitionId=1&branchName=master)|
|Windows Debug |[![Build Status](https://dev.azure.com/debauchee/Barrier/_apis/build/status/debauchee.barrier?branchName=master&jobName=Windows%20Build&configuration=Windows%20Build%20Debug)](https://dev.azure.com/debauchee/Barrier/_build/latest?definitionId=1&branchName=master)|
|Windows Release|[![Build Status](https://dev.azure.com/debauchee/Barrier/_apis/build/status/debauchee.barrier?branchName=master&jobName=Windows%20Build&configuration=Windows%20Build%20Release%20with%20Release%20Installer)](https://dev.azure.com/debauchee/Barrier/_build/latest?definitionId=1&branchName=master)|
|Snap |[![Snap Status](https://build.snapcraft.io/badge/debauchee/barrier.svg)](https://build.snapcraft.io/user/debauchee/barrier)|
### Build from source
@@ -51,6 +67,8 @@ apt install -y git
apt install -y openssh-server
## Installing x2x
apt install -y x2x
## Installing barrier
apt install -y barrier
## Installing chromium
wget https://github.com/RobRich999/Chromium_Clang/releases/download/v94.0.4585.0-r904940-linux64-deb-avx/chromium-browser-unstable_94.0.4585.0-1_amd64.deb
apt install -y ./chromium-browser-unstable_94.0.4585.0-1_amd64.deb
@@ -117,11 +135,13 @@ cd /path/.local/share/Steam/steamapps/common/X-Plane\ 11/
```
#### Open config file
```bash
{
"barrierhostname": "<barrier host name>",
"ipaddress": "0.0.0.0",
"rooms": "<path to room.json file>",
"scripttoexecute": "<path to script to execute (In case the Xplane 11 script)>",
"sshpassword": "<SSH password for x2x>",
"systemusername": "<system username>"
}
```

View File

@@ -8,23 +8,23 @@ import (
var (
defaultPath string
defaults = map[string]interface{}{
"SystemUsername": "",
defaults = map[string]interface{}{
"SystemUsername": "",
"BarrierHostName": "",
}
configName = "config"
configType = "json"
configFile = "config.json"
configName = "config"
configType = "json"
configFile = "config.json"
configPaths []string
)
type Config struct {
SystemUsername string
BarrierHostName string
Rooms string
IPAddress string
ScriptToExecute string
SSHPassword string
SystemUsername string
BarrierHostName string
Rooms string
IPAddress string
ScriptToExecute string
SSHPassword string
}
// Exists reports whether the named file or directory exists.
@@ -47,7 +47,7 @@ func SetDefaults() error {
defaultPath = curDir + "/"
// Get system username
user ,err := user.Current()
user, err := user.Current()
if err != nil {
return err
}
@@ -59,8 +59,8 @@ func SetDefaults() error {
}
//Setting default paths for the config file
defaults["SystemUsername"] = user.Username
defaults["BarrierHostName"] = name
defaults["SystemUsername"] = user.Username
defaults["BarrierHostName"] = name
defaults["Rooms"] = defaultPath + "room.json"
defaults["IPAddress"] = "0.0.0.0"
defaults["ScriptToExecute"] = ""
@@ -88,7 +88,7 @@ func SetDefaults() error {
return nil
}
func ConfigInit()(*Config,error) {
func ConfigInit() (*Config, error) {
curDir := os.Getenv("REMOTEGAMING")
//Setting current directory to default path
@@ -97,7 +97,7 @@ func ConfigInit()(*Config,error) {
configPaths = append(configPaths, defaultPath)
//Add all possible configurations paths
for _,v := range configPaths {
for _, v := range configPaths {
viper.AddConfigPath(v)
}
@@ -105,23 +105,23 @@ func ConfigInit()(*Config,error) {
if err := viper.ReadInConfig(); err != nil {
// If the error thrown is config file not found
//Sets default configuration to viper
for k,v := range defaults {
viper.SetDefault(k,v)
for k, v := range defaults {
viper.SetDefault(k, v)
}
viper.SetConfigName(configName)
viper.SetConfigFile(configFile)
viper.SetConfigType(configType)
if err = viper.WriteConfig(); err != nil {
return nil,err
return nil, err
}
}
// Adds configuration to the struct
var config Config
if err := viper.Unmarshal(&config); err != nil {
return nil,err
return nil, err
}
return &config,nil
return &config, nil
}

View File

@@ -1,4 +1,3 @@
package config
import (
@@ -6,7 +5,7 @@ import (
)
func TestConfigInit(t *testing.T) {
_,err := ConfigInit()
_, err := ConfigInit()
if err != nil {
t.Error(err)
}

View File

@@ -5,20 +5,20 @@
package core
import (
"laplace/config"
"github.com/Akilan1999/remotegameplay/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
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 {
func (b *Barrier) CreateBarrierSession() error {
//Checks if barrier client exists
if err := DetectBarrier(); err != nil {
@@ -31,7 +31,7 @@ func (b *Barrier)CreateBarrierSession() error {
return err
}
cmd := exec.Command("sudo","-u",configResp.SystemUsername,"barrierc","-f","--debug", "DEBUG" ,"--log", "/tmp/barrier.log",b.IPAddress)
cmd := exec.Command("sudo", "-u", configResp.SystemUsername, "barrierc", "-f", "--debug", "DEBUG", "--log", "/tmp/barrier.log", b.IPAddress)
// USE THE FOLLOWING TO DEBUG
//cmdReader, err := cmd.StdoutPipe()
@@ -62,18 +62,17 @@ func (b *Barrier)CreateBarrierSession() error {
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 {
func (b *Barrier) DeleteBarrierSession() error {
// Halts the process
cmd := exec.Command("pkill" ,"barrierc")
cmd := exec.Command("pkill", "barrierc")
if err := cmd.Run(); err != nil {
return err
return err
}
return nil

View File

@@ -1,497 +1,497 @@
package core
import (
"fmt"
"math/rand"
"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",
}
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",
}
// 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
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
}

View File

@@ -1,140 +1,139 @@
package core
import (
"encoding/json"
"fmt"
"github.com/gorilla/websocket"
"io/ioutil"
"laplace/config"
"os"
"encoding/json"
"fmt"
"github.com/Akilan1999/remotegameplay/config"
"github.com/gorilla/websocket"
"io/ioutil"
"os"
)
type Room struct {
ID string `json:"id"`
Sessions map[string]*StreamSession
CallerConn *websocket.Conn
BarrierSession *Barrier
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
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]
return roomMap[id]
}
func NewRoom(callerConn *websocket.Conn) *Room {
room := Room{
ID: newRoomID(),
Sessions: make(map[string]*StreamSession),
CallerConn: callerConn,
}
roomMap[room.ID] = &room
room := Room{
ID: newRoomID(),
Sessions: make(map[string]*StreamSession),
CallerConn: callerConn,
}
roomMap[room.ID] = &room
err := room.writeToFile()
if err != nil {
println(err)
}
err := room.writeToFile()
if err != nil {
println(err)
}
return &room
return &room
}
func newRoomID() string {
id := GetRandomName(0)
for GetRoom(id) != nil {
id = GetRandomName(0)
}
return id
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()
roomMap[id] = nil
// Writes nil to the file
roomMap[id].writeToFile()
}
func (room *Room) GetSession(id string) *StreamSession {
return room.Sessions[id]
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,
}
session := StreamSession{
ID: room.newSessionID(),
CallerIceCandidates: []string{},
CalleeIceCandidates: []string{},
CallerConn: room.CallerConn,
CalleeConn: calleeConn,
}
room.Sessions[session.ID] = &session
room.Sessions[session.ID] = &session
return &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
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
}
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
}
// 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
}
// Write room struct to json file
err = ioutil.WriteFile(config.Rooms, file, 0644)
if err != nil {
return err
}
return nil
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
}
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()
// 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)
// read our opened xmlFile as a byte array.
byteValue, _ := ioutil.ReadAll(jsonFile)
// we initialize our Users array
var rooms *Room
// 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)
// we unmarshal our byteArray which contains our
// jsonFile's content into 'users' which we defined above
json.Unmarshal(byteValue, &rooms)
return rooms, nil
return rooms, nil
}

View File

@@ -1,210 +1,210 @@
package core
import (
"fmt"
"github.com/gorilla/websocket"
"laplace/config"
"log"
"net/http"
"time"
"fmt"
"github.com/Akilan1999/remotegameplay/config"
"github.com/gorilla/websocket"
"log"
"net/http"
"time"
)
type WSMessage struct {
SessionID string
Type string
Value string
SessionID string
Type string
Value string
}
var upgrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
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
}
}
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 := http.NewServeMux()
server.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("files/static"))))
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")
})
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) {
//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
}
// Read hostname from config file
configResp, err := config.ConfigInit()
if err != nil {
print(err)
return
}
fmt.Fprintf(w, configResp.SystemUsername)
})
fmt.Fprintf(w, configResp.SystemUsername)
})
//Get SSH password of the current machine
server.HandleFunc("/SSHPassword", func(w http.ResponseWriter, r *http.Request) {
//Get SSH password of the current machine
server.HandleFunc("/SSHPassword", func(w http.ResponseWriter, r *http.Request) {
// Read hostname from config file
configResp, err := config.ConfigInit()
if err != nil {
print(err)
return
}
// Read hostname from config file
configResp, err := config.ConfigInit()
if err != nil {
print(err)
return
}
fmt.Fprintf(w, configResp.SSHPassword)
})
fmt.Fprintf(w, configResp.SSHPassword)
})
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
}
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)
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()
}
// 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,
})
}
}()
for sID, s := range r.Sessions {
_ = s.CalleeConn.WriteJSON(WSMessage{
Type: "roomClosed",
SessionID: sID,
})
}
}()
go sendHeartBeatWS(ticker, conn, quit)
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)
})
//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)
server.HandleFunc("/ws_connect", func(writer http.ResponseWriter, request *http.Request) {
conn, _ := upgrader.Upgrade(writer, request, nil)
ids, ok := request.URL.Query()["id"]
ids, ok := request.URL.Query()["id"]
if !ok || ids[0] == "" {
return
}
if !ok || ids[0] == "" {
return
}
room := GetRoom(ids[0])
room := GetRoom(ids[0])
ip, ok := request.URL.Query()["barrierip"]
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 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
}
if room == nil {
_ = conn.WriteJSON(WSMessage{
Type: "roomNotFound",
})
return
}
session := room.NewSession(conn)
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 := 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
}
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)
})
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
return server
}

View File

@@ -3,19 +3,20 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>Laplace</title>
<title>RemoteGamePlay</title>
<link rel="stylesheet" href="/static/bootstrap.min.css">
<link rel="stylesheet" href="/static/main.css?v=0.0.5">
</head>
<body>
<nav class="navbar">
<a class="navbar-brand text-dark" href="/">Laplace</a>
<a class="navbar-brand text-dark" href="/">RemoteGamePlay</a>
<span class="navbar-brand header-room" id="room-text"></span>
</nav>
<div class="container">
<div class="panel" id="panel">
<p> based on the open source project Laplace </p>
<p class="help-text">
<b>Laplace</b> is an open-source project to enable screen sharing directly via browser.
Made possible using WebRTC for low latency peer-to-peer connections,
@@ -45,18 +46,18 @@
<div class="form-group">
<input id="inputRoomID" type="text" class="form-control form-control-lg text-center" placeholder="Enter room ID" required>
</div>
<!-- <h4>Connect to Barrier KVM (Beta)</h4>-->
<!-- <p class="help-text">-->
<!-- This is a feature is still on beta testing. This feature will ensure that you can share the keyboard-->
<!-- and mouse of the server machine.-->
<!-- <br>-->
<!-- <span class="text-success font-weight-bold"> Note: This only works if the laplace server has barrierc installed and the room name you are connecting-->
<!-- too belongs to the server.</span>-->
<!-- </p>-->
<!-- <h6>Host Name: <span id="hostname"> Not detected </span></h6>-->
<!-- <div class="form-group">-->
<!-- <input id="barrierIP" type="text" class="form-control form-control-lg text-center" placeholder="IPV6 or IPV4 address">-->
<!-- </div>-->
<h4>Connect to Barrier KVM (Beta)</h4>
<p class="help-text">
This is a feature is still on beta testing. This feature will ensure that you can share the keyboard
and mouse of the server machine.
<br>
<span class="text-success font-weight-bold"> Note: This only works if the laplace server has Barrierc installed and the room name you are connecting
too belongs to the server.</span>
</p>
<h6>Host Name: <span id="hostname"> Not detected </span></h6>
<div class="form-group">
<input id="barrierIP" type="text" class="form-control form-control-lg text-center" placeholder="IPV6 or IPV4 address">
</div>
<button type="submit" class="btn btn-dark btn-block" value="submit">Join</button>
</form>
</div>
@@ -181,8 +182,8 @@
<div class="container">
<hr>
<div class="text-center small" id="footer">
<span>Laplace Project by Adam Jordan.</span>
<a href="https://github.com/adamyordan/laplace">Source Code</a>
<span><a href="https://github.com/Akilan1999/remotegameplay">RemoteGamePlay</a> Project by <a href="https://akilan.io">Akilan Selvacoumar</a> based on the fork
of <a href="https://github.com/Akilan1999/laplace/tree/keyboard_mouse">Laplace</a>.</span>
</div>
</div>

2
go.mod
View File

@@ -1,4 +1,4 @@
module laplace
module github.com/Akilan1999/remotegameplay
go 1.16

19
main.go
View File

@@ -4,8 +4,8 @@ import (
"encoding/json"
"flag"
"fmt"
"laplace/config"
"laplace/core"
"github.com/Akilan1999/remotegameplay/config"
"github.com/Akilan1999/remotegameplay/core"
"log"
"math/rand"
"net/http"
@@ -15,6 +15,7 @@ import (
func main() {
addr := flag.String("addr", "0.0.0.0:443", "Listen address")
port := flag.String("port", "8888", "port for running the server")
tls := flag.Bool("tls", false, "Use TLS")
setconfig := flag.Bool("setconfig", false, "Generates a config file")
certFile := flag.String("certFile", "files/server.crt", "TLS cert file")
@@ -23,7 +24,7 @@ func main() {
roomInfo := flag.Bool("roomInfo", false, "Getting room id of headless server")
killServer := flag.Bool("killServer", false, "Kills the laplace")
killChromium := flag.Bool("killChromium", false, "Kills all chromuim")
BinaryToExcute := flag.String("BinaryToExecute","","Providing path (i.e Absolute path) of binary to execute")
BinaryToExcute := flag.String("BinaryToExecute", "", "Providing path (i.e Absolute path) of binary to execute")
flag.Parse()
@@ -47,7 +48,6 @@ func main() {
return
}
// Running in headless mode
if *headless {
Config, err := config.ConfigInit()
@@ -75,7 +75,7 @@ func main() {
}
// Starting screen share headless
cmd := exec.Command("chromium-browser" ,"--no-sandbox","--auto-select-desktop-capture-source=Entire screen","--url","https://" + Addr + ":8888/?mode=headless","--ignore-certificate-errors")
cmd := exec.Command("chromium-browser", "--no-sandbox", "--auto-select-desktop-capture-source=Entire screen", "--url", "https://"+Addr+":"+*port+"/?mode=headless", "--ignore-certificate-errors")
if err := cmd.Start(); err != nil {
log.Fatalln(err)
}
@@ -96,22 +96,21 @@ func main() {
// kills laplace server
if *killServer {
cmd := exec.Command("pkill" ,"laplace")
cmd := exec.Command("pkill", "remotegameplay")
if err := cmd.Run(); err != nil {
fmt.Println(err)
}
return
}
// kills chromium server
// kills chromium server
if *killChromium {
cmd := exec.Command("pkill" ,"chromium")
cmd := exec.Command("pkill", "chromium")
if err := cmd.Run(); err != nil {
fmt.Println(err)
}
return
}
if *tls {
log.Println("Listening on TLS:", *addr)
if err := http.ListenAndServeTLS(*addr, *certFile, *keyFile, server); err != nil {
@@ -155,7 +154,7 @@ func Ip4or6(s string) string {
func RunTask(task string) error {
// Halts the process
cmd := exec.Command("sh",task)
cmd := exec.Command("sh", task)
if err := cmd.Start(); err != nil {
return err
}

10
run.sh
View File

@@ -1,11 +1,11 @@
# Starts server and runs it silently (i.e no output in the terminal)
./laplace -tls -addr 0.0.0.0:8888 > /dev/null 2>&1 &
./remotegameplay -tls -addr 0.0.0.0:${2} > /dev/null 2>&1 &
# Starts chromium browser and runs it silently (i.e no output in the terminal)
./laplace -headless -addr ${1} > /dev/null 2>&1 &
./remotegameplay -headless -addr ${1} -port ${2} > /dev/null 2>&1 &
# Lets the script sleep for 2 seconds
sleep 2
# Gets roomInfo and stores in the tmp directory as JSON
./laplace -headless -roomInfo > /tmp/output.txt
./remotegameplay -headless -roomInfo > /tmp/output.txt
# Gets the room ID from the JSON file and outputs to tmp directory as text
jq .id /tmp/output.txt > /tmp/test.txt
# Gets roomID from tmp directory as text
@@ -15,9 +15,9 @@ roomID=$(echo "$roomID" | tr -d '"')
# Checks if the IP address is a IPV6 or IPV4 address
if [ "$1" != "${1#*[0-9].[0-9]}" ]; then
echo "https://"${1}":8888/?roomID="${roomID}
echo "https://"${1}":${2}/?roomID="${roomID}
elif [ "$1" != "${1#*:[0-9a-fA-F]}" ]; then
echo "https://["${1}"]:8888/?roomID="${roomID}
echo "https://["${1}"]:${2}/?roomID="${roomID}
else
echo "Unrecognized IP format '$1'"
fi