Compare commits
14 Commits
fa9c53eb35
...
dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b743dab332 | ||
|
|
549a98bc48 | ||
|
|
4d5476d159 | ||
|
|
e0751b368a | ||
|
|
0f71b02271 | ||
|
|
f9b4269ce3 | ||
|
|
0475dd5766 | ||
|
|
a318876eb3 | ||
|
|
f20f01e6d6 | ||
|
|
93a9f2e5c9 | ||
|
|
d5916ac2be | ||
|
|
7d515bcd94 | ||
|
|
49cc5e1ab9 | ||
|
|
c9a0e6ee29 |
20
.github/workflows/docker-bake.hcl
vendored
@@ -3,14 +3,14 @@ variable "BASE_IMAGE" {
|
||||
}
|
||||
|
||||
group "default" {
|
||||
targets = ["runner"]
|
||||
targets = ["runner-base", "runner-builder"]
|
||||
}
|
||||
|
||||
target "runner-base" {
|
||||
dockerfile = "containerfiles/runner-base.Containerfile"
|
||||
context = "."
|
||||
args = {
|
||||
BASE_IMAGE = "${BASE_IMAGE}"
|
||||
BASE_IMAGE = BASE_IMAGE
|
||||
}
|
||||
cache-from = ["type=gha,scope=runner-base-pr"]
|
||||
cache-to = ["type=gha,scope=runner-base-pr,mode=max"]
|
||||
@@ -30,19 +30,3 @@ target "runner-builder" {
|
||||
runner-base = "target:runner-base"
|
||||
}
|
||||
}
|
||||
|
||||
target "runner" {
|
||||
dockerfile = "containerfiles/runner.Containerfile"
|
||||
context = "."
|
||||
args = {
|
||||
RUNNER_BASE_IMAGE = "runner-base:latest"
|
||||
RUNNER_BUILDER_IMAGE = "runner-builder:latest"
|
||||
}
|
||||
cache-from = ["type=gha,scope=runner-pr"]
|
||||
cache-to = ["type=gha,scope=runner-pr,mode=max"]
|
||||
tags = ["nestri-runner"]
|
||||
contexts = {
|
||||
runner-base = "target:runner-base"
|
||||
runner-builder = "target:runner-builder"
|
||||
}
|
||||
}
|
||||
|
||||
83
.github/workflows/play-standalone.yml
vendored
Normal file
@@ -0,0 +1,83 @@
|
||||
name: Build Nestri standalone playsite
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- "containerfiles/playsite.Containerfile"
|
||||
- ".github/workflows/play-standalone.yml"
|
||||
- "packages/play-standalone/**"
|
||||
- "packages/input/**"
|
||||
push:
|
||||
branches: [ dev, production ]
|
||||
paths:
|
||||
- "containerfiles/playsite.Containerfile"
|
||||
- ".github/workflows/play-standalone.yml"
|
||||
- "packages/play-standalone/**"
|
||||
- "packages/input/**"
|
||||
tags:
|
||||
- v*.*.*
|
||||
release:
|
||||
types: [ created ]
|
||||
|
||||
env:
|
||||
REGISTRY: ghcr.io
|
||||
IMAGE_NAME: nestrilabs/nestri
|
||||
BASE_TAG_PREFIX: playsite
|
||||
|
||||
jobs:
|
||||
build-docker-pr:
|
||||
name: Build image on PR
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
- name: Build Docker image
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
file: containerfiles/playsite.Containerfile
|
||||
context: ./
|
||||
push: false
|
||||
load: true
|
||||
tags: nestri:playsite
|
||||
|
||||
build-and-push-docker:
|
||||
name: Build and push image
|
||||
if: ${{ github.ref == 'refs/heads/dev' || github.ref == 'refs/heads/production' }}
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v4
|
||||
- name: Log into registry ${{ env.REGISTRY }}
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ github.token }}
|
||||
- name: Extract Container metadata
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/${{ env.BASE_TAG_PREFIX }}
|
||||
#
|
||||
#tag on release, and a nightly build for 'dev'
|
||||
tags: |
|
||||
type=raw,value=nightly,enable={{is_default_branch}}
|
||||
type=raw,value={{branch}}
|
||||
type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', 'production') }}
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
type=semver,pattern={{major}}
|
||||
- name: Build Docker image
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
file: containerfiles/playsite.Containerfile
|
||||
context: ./
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
3
.github/workflows/relay.yml
vendored
@@ -1,6 +1,5 @@
|
||||
#Tabs not spaces, you moron :)
|
||||
name: Build Nestri relay
|
||||
|
||||
name: Build nestri:relay
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
|
||||
73
.github/workflows/runner-bases.yml
vendored
Normal file
@@ -0,0 +1,73 @@
|
||||
name: Build Nestri runner base images
|
||||
|
||||
on: [ workflow_call ]
|
||||
|
||||
env:
|
||||
REGISTRY: ghcr.io
|
||||
IMAGE_NAME: nestrilabs/nestri
|
||||
BASE_IMAGE: docker.io/cachyos/cachyos:latest
|
||||
|
||||
jobs:
|
||||
build-and-push-bases:
|
||||
name: Build and push images
|
||||
if: ${{ github.ref == 'refs/heads/production' || github.ref == 'refs/heads/dev' }}
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
strategy:
|
||||
matrix:
|
||||
variant:
|
||||
- { suffix: "v2", base: "docker.io/cachyos/cachyos:latest" }
|
||||
- { suffix: "v3", base: "docker.io/cachyos/cachyos-v3:latest" }
|
||||
#- { suffix: "v4", base: "docker.io/cachyos/cachyos-v4:latest" } # Disabled until GHA has this
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v4
|
||||
- name: Log into registry ${{ env.REGISTRY }}
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ github.token }}
|
||||
- name: Setup Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
- name: Set Swap Space
|
||||
uses: pierotofy/set-swap-space@master
|
||||
with:
|
||||
swap-size-gb: 20
|
||||
- name: Build and push runner-base image
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
file: containerfiles/runner-base.Containerfile
|
||||
context: ./
|
||||
push: true
|
||||
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/runner-base:latest-${{ matrix.variant.suffix }}
|
||||
build-args: |
|
||||
BASE_IMAGE=${{ matrix.variant.base }}
|
||||
cache-from: type=gha,scope=runner-base-${{ matrix.variant.suffix }},mode=max
|
||||
cache-to: type=gha,scope=runner-base-${{ matrix.variant.suffix }},mode=max
|
||||
pull: true
|
||||
- name: Build and push runner-builder image
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
file: containerfiles/runner-builder.Containerfile
|
||||
context: ./
|
||||
push: true
|
||||
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/runner-builder:latest-${{ matrix.variant.suffix }}
|
||||
build-args: |
|
||||
RUNNER_BASE_IMAGE=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/runner-base:latest-${{ matrix.variant.suffix }}
|
||||
cache-from: type=gha,scope=runner-builder-${{ matrix.variant.suffix }},mode=max
|
||||
cache-to: type=gha,scope=runner-builder-${{ matrix.variant.suffix }},mode=max
|
||||
- name: Build and push runner-common image
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
file: containerfiles/runner-common.Containerfile
|
||||
context: ./
|
||||
push: true
|
||||
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/runner-common:latest-${{ matrix.variant.suffix }}
|
||||
build-args: |
|
||||
RUNNER_BASE_IMAGE=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/runner-base:latest-${{ matrix.variant.suffix }}
|
||||
RUNNER_BUILDER_IMAGE=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/runner-builder:latest-${{ matrix.variant.suffix }}
|
||||
cache-from: type=gha,scope=runner-common-${{ matrix.variant.suffix }},mode=max
|
||||
cache-to: type=gha,scope=runner-common-${{ matrix.variant.suffix }},mode=max
|
||||
86
.github/workflows/runner-variants.yml
vendored
Normal file
@@ -0,0 +1,86 @@
|
||||
name: Build Nestri runner image variants
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: 7 0 * * 1,3,6 # Nightlies
|
||||
push:
|
||||
branches: [ dev, production ]
|
||||
paths:
|
||||
- "containerfiles/*runner.Containerfile"
|
||||
- ".github/workflows/runner-variants.yml"
|
||||
- "packages/scripts/**"
|
||||
- "packages/configs/**"
|
||||
tags:
|
||||
- v*.*.*
|
||||
release:
|
||||
types: [ created ]
|
||||
|
||||
env:
|
||||
REGISTRY: ghcr.io
|
||||
IMAGE_NAME: nestrilabs/nestri
|
||||
|
||||
jobs:
|
||||
bases:
|
||||
uses: ./.github/workflows/runner-bases.yml
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
build-and-push-variants:
|
||||
needs: [ bases ]
|
||||
name: Build and push images
|
||||
if: ${{ github.ref == 'refs/heads/production' || github.ref == 'refs/heads/dev' }}
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
strategy:
|
||||
matrix:
|
||||
variant:
|
||||
- { suffix: "v2", base: "docker.io/cachyos/cachyos:latest" }
|
||||
- { suffix: "v3", base: "docker.io/cachyos/cachyos-v3:latest" }
|
||||
#- { suffix: "v4", base: "docker.io/cachyos/cachyos-v4:latest" } # Disabled until GHA has this
|
||||
runner:
|
||||
- steam
|
||||
- heroic
|
||||
- minecraft
|
||||
# ADD MORE HERE AS NEEDED #
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v4
|
||||
- name: Log into registry ${{ env.REGISTRY }}
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ github.token }}
|
||||
- name: Extract runner metadata
|
||||
id: meta-runner
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/runner
|
||||
tags: |
|
||||
type=raw,value=nightly-${{ matrix.runner }}-${{ matrix.variant.suffix }},enable={{is_default_branch}}
|
||||
type=raw,value={{branch}}-${{ matrix.runner }}-${{ matrix.variant.suffix }}
|
||||
type=raw,value=latest-${{ matrix.runner }}-${{ matrix.variant.suffix }},enable=${{ github.ref == format('refs/heads/{0}', 'production') }}
|
||||
type=semver,pattern={{version}}-${{ matrix.runner }}-${{ matrix.variant.suffix }}
|
||||
type=semver,pattern={{major}}.{{minor}}-${{ matrix.runner }}-${{ matrix.variant.suffix }}
|
||||
type=semver,pattern={{major}}-${{ matrix.runner }}-${{ matrix.variant.suffix }}
|
||||
- name: Setup Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
- name: Set Swap Space
|
||||
uses: pierotofy/set-swap-space@master
|
||||
with:
|
||||
swap-size-gb: 20
|
||||
- name: Build and push runner image
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
file: containerfiles/${{ matrix.runner }}-runner.Containerfile
|
||||
context: ./
|
||||
push: true
|
||||
tags: ${{ steps.meta-runner.outputs.tags }}
|
||||
labels: ${{ steps.meta-runner.outputs.labels }}
|
||||
build-args: |
|
||||
RUNNER_COMMON_IMAGE=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/runner-common:latest-${{ matrix.variant.suffix }}
|
||||
cache-from: type=gha,scope=runner-${{ matrix.runner }}-${{ matrix.variant.suffix }},mode=max
|
||||
cache-to: type=gha,scope=runner-${{ matrix.runner }}-${{ matrix.variant.suffix }},mode=max
|
||||
148
.github/workflows/runner.yml
vendored
@@ -1,148 +0,0 @@
|
||||
#Tabs not spaces, you moron :)
|
||||
|
||||
name: Build nestri-runner
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- "containerfiles/runner*.Containerfile"
|
||||
- "packages/scripts/**"
|
||||
- "packages/server/**"
|
||||
- ".github/workflows/runner.yml"
|
||||
schedule:
|
||||
- cron: 7 0 * * 1,3,6 # Regularly to keep that build cache warm
|
||||
push:
|
||||
branches: [dev, production]
|
||||
paths:
|
||||
- "containerfiles/runner*.Containerfile"
|
||||
- ".github/workflows/runner.yml"
|
||||
- "packages/scripts/**"
|
||||
- "packages/server/**"
|
||||
tags:
|
||||
- v*.*.*
|
||||
release:
|
||||
types: [created]
|
||||
|
||||
env:
|
||||
REGISTRY: ghcr.io
|
||||
IMAGE_NAME: nestrilabs/nestri
|
||||
BASE_IMAGE: docker.io/cachyos/cachyos:latest
|
||||
|
||||
# This makes our release ci quit prematurely
|
||||
# concurrency:
|
||||
# group: ci-${{ github.ref }}
|
||||
# cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build-docker-pr:
|
||||
name: Build images on PR
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
steps:
|
||||
-
|
||||
name: Setup Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
-
|
||||
name: Set Swap Space
|
||||
uses: pierotofy/set-swap-space@master
|
||||
with:
|
||||
swap-size-gb: 20
|
||||
-
|
||||
name: Build images using bake
|
||||
uses: docker/bake-action@v6
|
||||
env:
|
||||
BASE_IMAGE: ${{ env.BASE_IMAGE }}
|
||||
with:
|
||||
files: |
|
||||
./.github/workflows/docker-bake.hcl
|
||||
targets: runner
|
||||
push: false
|
||||
load: true
|
||||
|
||||
build-and-push-docker:
|
||||
name: Build and push images
|
||||
if: ${{ github.ref == 'refs/heads/production' || github.ref == 'refs/heads/dev' }}
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
strategy:
|
||||
matrix:
|
||||
variant:
|
||||
- { suffix: "", base: "docker.io/cachyos/cachyos:latest" }
|
||||
- { suffix: "-v3", base: "docker.io/cachyos/cachyos-v3:latest" }
|
||||
#- { suffix: "-v4", base: "docker.io/cachyos/cachyos-v4:latest" } # Disabled until GHA has this
|
||||
steps:
|
||||
-
|
||||
name: Checkout repo
|
||||
uses: actions/checkout@v4
|
||||
-
|
||||
name: Log into registry ${{ env.REGISTRY }}
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ github.token }}
|
||||
-
|
||||
name: Extract runner metadata
|
||||
id: meta-runner
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/runner
|
||||
tags: |
|
||||
type=raw,value=nightly${{ matrix.variant.suffix }},enable={{is_default_branch}}
|
||||
type=raw,value={{branch}}${{ matrix.variant.suffix }}
|
||||
type=raw,value=latest${{ matrix.variant.suffix }},enable=${{ github.ref == format('refs/heads/{0}', 'production') }}
|
||||
type=semver,pattern={{version}}${{ matrix.variant.suffix }}
|
||||
type=semver,pattern={{major}}.{{minor}}${{ matrix.variant.suffix }}
|
||||
type=semver,pattern={{major}}${{ matrix.variant.suffix }}
|
||||
-
|
||||
name: Setup Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
-
|
||||
name: Set Swap Space
|
||||
uses: pierotofy/set-swap-space@master
|
||||
with:
|
||||
swap-size-gb: 20
|
||||
-
|
||||
name: Build and push runner-base image
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
file: containerfiles/runner-base.Containerfile
|
||||
context: ./
|
||||
push: true
|
||||
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/runner-base:latest${{ matrix.variant.suffix }}
|
||||
build-args: |
|
||||
BASE_IMAGE=${{ matrix.variant.base }}
|
||||
cache-from: type=gha,scope=runner-base${{ matrix.variant.suffix }},mode=max
|
||||
cache-to: type=gha,scope=runner-base${{ matrix.variant.suffix }},mode=max
|
||||
pull: ${{ github.event_name == 'schedule' }}
|
||||
-
|
||||
name: Build and push runner-builder image
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
file: containerfiles/runner-builder.Containerfile
|
||||
context: ./
|
||||
push: true
|
||||
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/runner-builder:latest${{ matrix.variant.suffix }}
|
||||
build-args: |
|
||||
RUNNER_BASE_IMAGE=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/runner-base:latest${{ matrix.variant.suffix }}
|
||||
cache-from: type=gha,scope=runner-builder${{ matrix.variant.suffix }},mode=max
|
||||
cache-to: type=gha,scope=runner-builder${{ matrix.variant.suffix }},mode=max
|
||||
-
|
||||
name: Build and push runner image
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
file: containerfiles/runner.Containerfile
|
||||
context: ./
|
||||
push: true
|
||||
tags: ${{ steps.meta-runner.outputs.tags }}
|
||||
labels: ${{ steps.meta-runner.outputs.labels }}
|
||||
build-args: |
|
||||
RUNNER_BASE_IMAGE=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/runner-base:latest${{ matrix.variant.suffix }}
|
||||
RUNNER_BUILDER_IMAGE=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/runner-builder:latest${{ matrix.variant.suffix }}
|
||||
cache-from: type=gha,scope=runner${{ matrix.variant.suffix }},mode=max
|
||||
cache-to: type=gha,scope=runner${{ matrix.variant.suffix }},mode=max
|
||||
@@ -15,3 +15,4 @@ plugins:
|
||||
# Rust (nestri-server)
|
||||
- remote: buf.build/community/neoeinstein-prost
|
||||
out: packages/server/src/proto
|
||||
opt: flat_output_dir=true
|
||||
|
||||
23
containerfiles/heroic-runner.Containerfile
Normal file
@@ -0,0 +1,23 @@
|
||||
# Container build arguments #
|
||||
ARG RUNNER_COMMON_IMAGE=runner-common:latest
|
||||
|
||||
#*********************#
|
||||
# Final Runtime Stage #
|
||||
#*********************#
|
||||
FROM ${RUNNER_COMMON_IMAGE}
|
||||
|
||||
### FLAVOR/VARIANT CONFIGURATION ###
|
||||
## HEROIC LAUNCHER ##
|
||||
RUN --mount=type=cache,target=/var/cache/pacman/pkg \
|
||||
pacman -S --noconfirm heroic-games-launcher-bin && \
|
||||
# Cleanup
|
||||
paccache -rk1 && \
|
||||
rm -rf /usr/share/{info,man,doc}/*
|
||||
|
||||
## FLAVOR/VARIANT LAUNCH COMMAND ##
|
||||
ENV NESTRI_LAUNCH_CMD="heroic"
|
||||
### END OF FLAVOR/VARIANT CONFIGURATION ###
|
||||
|
||||
### REQUIRED DEFAULT ENTRYPOINT FOR FLAVOR/VARIANT ###
|
||||
USER root
|
||||
ENTRYPOINT ["supervisord", "-c", "/etc/nestri/supervisord.conf"]
|
||||
24
containerfiles/minecraft-runner.Containerfile
Normal file
@@ -0,0 +1,24 @@
|
||||
# Container build arguments #
|
||||
ARG RUNNER_COMMON_IMAGE=runner-common:latest
|
||||
|
||||
#*********************#
|
||||
# Final Runtime Stage #
|
||||
#*********************#
|
||||
FROM ${RUNNER_COMMON_IMAGE}
|
||||
|
||||
### FLAVOR/VARIANT CONFIGURATION ###
|
||||
## MINECRAFT ##
|
||||
RUN --mount=type=cache,target=/var/cache/pacman/pkg \
|
||||
pacman -S --noconfirm paru && \
|
||||
sudo -H -u ${NESTRI_USER} paru -S --noconfirm aur/minecraft-launcher && \
|
||||
# Cleanup
|
||||
paccache -rk1 && \
|
||||
rm -rf /usr/share/{info,man,doc}/*
|
||||
|
||||
## FLAVOR/VARIANT LAUNCH COMMAND ##
|
||||
ENV NESTRI_LAUNCH_CMD="minecraft-launcher"
|
||||
### END OF FLAVOR/VARIANT CONFIGURATION ###
|
||||
|
||||
### REQUIRED DEFAULT ENTRYPOINT FOR FLAVOR/VARIANT ###
|
||||
USER root
|
||||
ENTRYPOINT ["supervisord", "-c", "/etc/nestri/supervisord.conf"]
|
||||
@@ -15,7 +15,7 @@ ENV CARGO_HOME=/usr/local/cargo \
|
||||
|
||||
# Install build essentials and caching tools
|
||||
RUN --mount=type=cache,target=/var/cache/pacman/pkg \
|
||||
pacman -Sy --noconfirm rustup git base-devel mold \
|
||||
pacman -S --noconfirm rustup git base-devel mold \
|
||||
meson pkgconf cmake git gcc make
|
||||
|
||||
# Override various linker with symlink so mold is forcefully used (ld, ld.lld, lld)
|
||||
@@ -28,7 +28,7 @@ RUN rustup default stable
|
||||
|
||||
# Install cargo-chef with proper caching
|
||||
RUN --mount=type=cache,target=${CARGO_HOME}/registry \
|
||||
cargo install -j $(nproc) cargo-chef --locked
|
||||
cargo install cargo-chef --locked
|
||||
|
||||
#*******************************#
|
||||
# vimputti manager build stages #
|
||||
@@ -38,7 +38,7 @@ WORKDIR /builder
|
||||
|
||||
# Install build dependencies
|
||||
RUN --mount=type=cache,target=/var/cache/pacman/pkg \
|
||||
pacman -Sy --noconfirm lib32-gcc-libs
|
||||
pacman -S --noconfirm lib32-gcc-libs
|
||||
|
||||
# Clone repository
|
||||
RUN git clone --depth 1 --rev "2fde5376b6b9a38cdbd94ccc6a80c9d29a81a417" https://github.com/DatCaptainHorse/vimputti.git
|
||||
@@ -83,7 +83,7 @@ WORKDIR /builder
|
||||
|
||||
# Install build dependencies
|
||||
RUN --mount=type=cache,target=/var/cache/pacman/pkg \
|
||||
pacman -Sy --noconfirm gst-plugins-good gst-plugin-rswebrtc
|
||||
pacman -S --noconfirm gst-plugins-good gst-plugin-rswebrtc
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
FROM nestri-server-deps AS nestri-server-planner
|
||||
@@ -123,14 +123,14 @@ WORKDIR /builder
|
||||
|
||||
# Install build dependencies
|
||||
RUN --mount=type=cache,target=/var/cache/pacman/pkg \
|
||||
pacman -Sy --noconfirm libxkbcommon wayland \
|
||||
pacman -S --noconfirm libxkbcommon wayland \
|
||||
gst-plugins-good gst-plugins-bad libinput
|
||||
|
||||
RUN --mount=type=cache,target=${CARGO_HOME}/registry \
|
||||
cargo install cargo-c
|
||||
|
||||
# Clone repository
|
||||
RUN git clone --depth 1 --rev "a4abcfe2cffe2d33b564d1308b58504a5e3012b1" https://github.com/games-on-whales/gst-wayland-display.git
|
||||
RUN git clone --depth 1 --rev "67b1183997fd7aaf57398e4b01bd64c4d2433c45" https://github.com/games-on-whales/gst-wayland-display.git
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
FROM gst-wayland-deps AS gst-wayland-planner
|
||||
@@ -148,7 +148,7 @@ COPY --from=gst-wayland-planner /builder/gst-wayland-display/recipe.json .
|
||||
|
||||
# Cache dependencies using cargo-chef
|
||||
RUN --mount=type=cache,target=${CARGO_HOME}/registry \
|
||||
cargo chef cook --release --recipe-path recipe.json
|
||||
cargo chef cook --release --recipe-path recipe.json --features cuda
|
||||
|
||||
|
||||
ENV CARGO_TARGET_DIR=/builder/target
|
||||
@@ -158,7 +158,7 @@ COPY --from=gst-wayland-planner /builder/gst-wayland-display/ .
|
||||
# Build and install directly to artifacts
|
||||
RUN --mount=type=cache,target=${CARGO_HOME}/registry \
|
||||
--mount=type=cache,target=/builder/target \
|
||||
cargo cinstall --prefix=${ARTIFACTS} --release
|
||||
cargo cinstall --prefix=${ARTIFACTS} --release --features cuda
|
||||
|
||||
#*********************************#
|
||||
# Patched bubblewrap build stages #
|
||||
@@ -168,7 +168,7 @@ WORKDIR /builder
|
||||
|
||||
# Install build dependencies
|
||||
RUN --mount=type=cache,target=/var/cache/pacman/pkg \
|
||||
pacman -Sy --noconfirm libtool libcap libselinux
|
||||
pacman -S --noconfirm libtool libcap libselinux
|
||||
|
||||
# Copy patch file from host
|
||||
COPY packages/patches/bubblewrap/ /builder/patches/
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
ARG RUNNER_BASE_IMAGE=runner-base:latest
|
||||
ARG RUNNER_BUILDER_IMAGE=runner-builder:latest
|
||||
|
||||
#*********************#
|
||||
# Final Runtime Stage #
|
||||
#*********************#
|
||||
#**********************#
|
||||
# Runtime Common Stage #
|
||||
#**********************#
|
||||
FROM ${RUNNER_BASE_IMAGE} AS runtime
|
||||
FROM ${RUNNER_BUILDER_IMAGE} AS builder
|
||||
FROM runtime
|
||||
@@ -12,11 +12,11 @@ FROM runtime
|
||||
### Package Installation ###
|
||||
# Core system components
|
||||
RUN --mount=type=cache,target=/var/cache/pacman/pkg \
|
||||
pacman -Sy --needed --noconfirm \
|
||||
pacman -S --needed --noconfirm \
|
||||
vulkan-intel lib32-vulkan-intel vpl-gpu-rt \
|
||||
vulkan-radeon lib32-vulkan-radeon \
|
||||
mesa lib32-mesa \
|
||||
steam gtk3 lib32-gtk3 \
|
||||
mesa lib32-mesa vulkan-mesa-layers lib32-vulkan-mesa-layers \
|
||||
gtk3 lib32-gtk3 \
|
||||
sudo xorg-xwayland seatd libinput gamescope mangohud wlr-randr \
|
||||
pipewire pipewire-pulse pipewire-alsa wireplumber \
|
||||
noto-fonts-cjk supervisor jq pacman-contrib \
|
||||
@@ -67,15 +67,8 @@ RUN mkdir -p /etc/pipewire/pipewire.conf.d && \
|
||||
COPY packages/configs/wireplumber.conf.d/* /etc/wireplumber/wireplumber.conf.d/
|
||||
COPY packages/configs/pipewire.conf.d/* /etc/pipewire/pipewire.conf.d/
|
||||
|
||||
## Steam Configs - Proton (Experimental flavor) ##
|
||||
RUN mkdir -p "${NESTRI_HOME}/.local/share/Steam/config"
|
||||
|
||||
COPY packages/configs/steam/config.vdf "${NESTRI_HOME}/.local/share/Steam/config/"
|
||||
|
||||
## MangoHud Config ##
|
||||
RUN mkdir -p "${NESTRI_HOME}/.config/MangoHud"
|
||||
|
||||
COPY packages/configs/MangoHud/MangoHud.conf "${NESTRI_HOME}/.config/MangoHud/"
|
||||
COPY packages/configs/MangoHud/MangoHud.conf /etc/nestri/configs/MangoHud/
|
||||
|
||||
### Artifacts from Builder ###
|
||||
COPY --from=builder /artifacts/bin/nestri-server /usr/bin/
|
||||
@@ -93,7 +86,3 @@ RUN chmod +x /etc/nestri/{envs.sh,entrypoint*.sh} && \
|
||||
setcap cap_net_admin+ep /usr/bin/vimputti-manager && \
|
||||
dbus-uuidgen > /etc/machine-id && \
|
||||
LANG=en_US.UTF-8 locale-gen
|
||||
|
||||
# Root for most container engines, nestri-user compatible for apptainer without fakeroot
|
||||
USER root
|
||||
ENTRYPOINT ["supervisord", "-c", "/etc/nestri/supervisord.conf"]
|
||||
27
containerfiles/steam-runner.Containerfile
Normal file
@@ -0,0 +1,27 @@
|
||||
# Container build arguments #
|
||||
ARG RUNNER_COMMON_IMAGE=runner-common:latest
|
||||
|
||||
#*********************#
|
||||
# Final Runtime Stage #
|
||||
#*********************#
|
||||
FROM ${RUNNER_COMMON_IMAGE}
|
||||
|
||||
### FLAVOR/VARIANT CONFIGURATION ###
|
||||
## STEAM ##
|
||||
RUN --mount=type=cache,target=/var/cache/pacman/pkg \
|
||||
pacman -S --noconfirm steam && \
|
||||
# Cleanup
|
||||
paccache -rk1 && \
|
||||
rm -rf /usr/share/{info,man,doc}/*
|
||||
|
||||
## Steam Configs - Proton (Experimental flavor) ##
|
||||
RUN mkdir -p "${NESTRI_HOME}/.local/share/Steam/config"
|
||||
COPY packages/configs/steam/config.vdf "${NESTRI_HOME}/.local/share/Steam/config/"
|
||||
|
||||
## FLAVOR/VARIANT LAUNCH COMMAND ##
|
||||
ENV NESTRI_LAUNCH_CMD="steam -tenfoot -cef-force-gpu"
|
||||
### END OF FLAVOR/VARIANT CONFIGURATION ###
|
||||
|
||||
### REQUIRED DEFAULT ENTRYPOINT FOR FLAVOR/VARIANT ###
|
||||
USER root
|
||||
ENTRYPOINT ["supervisord", "-c", "/etc/nestri/supervisord.conf"]
|
||||
@@ -2,6 +2,6 @@ context.properties = {
|
||||
default.clock.rate = 48000
|
||||
default.clock.allowed-rates = [48000]
|
||||
default.clock.min-quantum = 128
|
||||
default.clock.max-quantum = 256
|
||||
default.clock.quantum = 128
|
||||
default.clock.max-quantum = 1024
|
||||
default.clock.quantum = 512
|
||||
}
|
||||
@@ -22,7 +22,7 @@
|
||||
],
|
||||
"apply_properties": {
|
||||
"pulse.min.req": 128,
|
||||
"pulse.max.req": 256,
|
||||
"pulse.max.req": 1024,
|
||||
"pulse.idle.timeout": 0
|
||||
}
|
||||
}
|
||||
|
||||
3
packages/input/.containerignore
Normal file
@@ -0,0 +1,3 @@
|
||||
.idea/
|
||||
dist/
|
||||
node_modules/
|
||||
@@ -36,10 +36,8 @@ export class Keyboard {
|
||||
if (this.connected) this.stop();
|
||||
|
||||
this.connected = true;
|
||||
document.addEventListener("keydown", this.keydownListener, {
|
||||
passive: false,
|
||||
});
|
||||
document.addEventListener("keyup", this.keyupListener, { passive: false });
|
||||
document.addEventListener("keydown", this.keydownListener);
|
||||
document.addEventListener("keyup", this.keyupListener);
|
||||
}
|
||||
|
||||
private stop() {
|
||||
@@ -73,6 +71,6 @@ export class Keyboard {
|
||||
private keyToVirtualKeyCode(code: string) {
|
||||
// Treat Home key as Escape - TODO: Make user-configurable
|
||||
if (code === "Home") return 1;
|
||||
return keyCodeToLinuxEventCode[code] || undefined;
|
||||
return keyCodeToLinuxEventCode[code] || 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,8 +35,6 @@ export class Mouse {
|
||||
this.wrtc = webrtc;
|
||||
this.canvas = canvas;
|
||||
|
||||
this.sendInterval = 1000 / webrtc.currentFrameRate;
|
||||
|
||||
this.mousemoveListener = (e: MouseEvent) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
@@ -77,18 +75,10 @@ export class Mouse {
|
||||
|
||||
if (document.pointerLockElement == this.canvas) {
|
||||
this.connected = true;
|
||||
this.canvas.addEventListener("mousemove", this.mousemoveListener, {
|
||||
passive: false,
|
||||
});
|
||||
this.canvas.addEventListener("mousedown", this.mousedownListener, {
|
||||
passive: false,
|
||||
});
|
||||
this.canvas.addEventListener("mouseup", this.mouseupListener, {
|
||||
passive: false,
|
||||
});
|
||||
this.canvas.addEventListener("wheel", this.mousewheelListener, {
|
||||
passive: false,
|
||||
});
|
||||
this.canvas.addEventListener("mousemove", this.mousemoveListener);
|
||||
this.canvas.addEventListener("mousedown", this.mousedownListener);
|
||||
this.canvas.addEventListener("mouseup", this.mouseupListener);
|
||||
this.canvas.addEventListener("wheel", this.mousewheelListener);
|
||||
} else {
|
||||
if (this.connected) {
|
||||
this.stop();
|
||||
@@ -106,7 +96,7 @@ export class Mouse {
|
||||
|
||||
private startProcessing() {
|
||||
setInterval(() => {
|
||||
if (this.connected && (this.movementX !== 0 || this.movementY !== 0)) {
|
||||
if (this.connected) {
|
||||
this.sendAggregatedMouseMove();
|
||||
this.movementX = 0;
|
||||
this.movementY = 0;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// @generated by protoc-gen-es v2.10.0 with parameter "target=ts"
|
||||
// @generated by protoc-gen-es v2.10.1 with parameter "target=ts"
|
||||
// @generated from file latency_tracker.proto (package proto, syntax proto3)
|
||||
/* eslint-disable */
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// @generated by protoc-gen-es v2.10.0 with parameter "target=ts"
|
||||
// @generated by protoc-gen-es v2.10.1 with parameter "target=ts"
|
||||
// @generated from file messages.proto (package proto, syntax proto3)
|
||||
/* eslint-disable */
|
||||
|
||||
@@ -90,6 +90,8 @@ export type ProtoMessage = Message<"proto.ProtoMessage"> & {
|
||||
case: "keyDown";
|
||||
} | {
|
||||
/**
|
||||
* ProtoClipboard clipboard = 9;
|
||||
*
|
||||
* @generated from field: proto.ProtoKeyUp key_up = 8;
|
||||
*/
|
||||
value: ProtoKeyUp;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// @generated by protoc-gen-es v2.10.0 with parameter "target=ts"
|
||||
// @generated by protoc-gen-es v2.10.1 with parameter "target=ts"
|
||||
// @generated from file types.proto (package proto, syntax proto3)
|
||||
/* eslint-disable */
|
||||
|
||||
|
||||
@@ -13,9 +13,10 @@ import {
|
||||
ProtoClientRequestRoomStream,
|
||||
ProtoClientRequestRoomStreamSchema,
|
||||
ProtoICE,
|
||||
ProtoICESchema, ProtoRaw,
|
||||
ProtoICESchema,
|
||||
ProtoRaw,
|
||||
ProtoSDP,
|
||||
ProtoSDPSchema
|
||||
ProtoSDPSchema,
|
||||
} from "./proto/types_pb";
|
||||
import { P2PMessageStream } from "./streamwrapper";
|
||||
|
||||
@@ -38,7 +39,6 @@ export class WebRTCStream {
|
||||
private _roomName: string | undefined = undefined;
|
||||
private _isConnected: boolean = false;
|
||||
private _dataChannelCallbacks: Array<(data: any) => void> = [];
|
||||
currentFrameRate: number = 100;
|
||||
|
||||
constructor(
|
||||
serverURL: string,
|
||||
@@ -126,11 +126,19 @@ export class WebRTCStream {
|
||||
}
|
||||
});
|
||||
|
||||
this._msgStream.on("session-assigned", (data: ProtoClientRequestRoomStream) => {
|
||||
this._msgStream.on(
|
||||
"session-assigned",
|
||||
(data: ProtoClientRequestRoomStream) => {
|
||||
this._sessionId = data.sessionId;
|
||||
localStorage.setItem("nestri-session-id", this._sessionId);
|
||||
console.log("Session ID assigned:", this._sessionId, "for room:", data.roomName);
|
||||
});
|
||||
console.log(
|
||||
"Session ID assigned:",
|
||||
this._sessionId,
|
||||
"for room:",
|
||||
data.roomName,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
this._msgStream.on("offer", async (data: ProtoSDP) => {
|
||||
if (!this._pc) {
|
||||
@@ -293,26 +301,8 @@ export class WebRTCStream {
|
||||
this._onConnected(
|
||||
new MediaStream([this._audioTrack, this._videoTrack]),
|
||||
);
|
||||
|
||||
// Continuously set low-latency target
|
||||
this._pc.getReceivers().forEach((receiver: RTCRtpReceiver) => {
|
||||
let intervalLoop = setInterval(async () => {
|
||||
if (
|
||||
receiver.track.readyState !== "live" ||
|
||||
(receiver.transport && receiver.transport.state !== "connected")
|
||||
) {
|
||||
clearInterval(intervalLoop);
|
||||
return;
|
||||
} else {
|
||||
// @ts-ignore
|
||||
receiver.jitterBufferTarget = receiver.jitterBufferDelayHint = receiver.playoutDelayHint = 0;
|
||||
}
|
||||
}, 50);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
this._gatherFrameRate();
|
||||
} else if (
|
||||
this._pc.connectionState === "failed" ||
|
||||
this._pc.connectionState === "closed" ||
|
||||
@@ -412,11 +402,16 @@ export class WebRTCStream {
|
||||
};
|
||||
}
|
||||
|
||||
private _gatherFrameRate() {
|
||||
if (this._pc === undefined || this._videoTrack === undefined) return;
|
||||
private async _gatherStats(): Promise<any> {
|
||||
if (
|
||||
this._pc === undefined ||
|
||||
this._videoTrack === undefined ||
|
||||
!this._isConnected
|
||||
)
|
||||
return null;
|
||||
|
||||
const videoInfoPromise = new Promise<{ fps: number }>((resolve) => {
|
||||
// Keep trying to get fps until it's found
|
||||
return new Promise<any>((resolve) => {
|
||||
// Keep trying to get stats until gotten
|
||||
const interval = setInterval(async () => {
|
||||
if (this._pc === undefined) {
|
||||
clearInterval(interval);
|
||||
@@ -428,15 +423,11 @@ export class WebRTCStream {
|
||||
if (report.type === "inbound-rtp") {
|
||||
clearInterval(interval);
|
||||
|
||||
resolve({ fps: report.framesPerSecond });
|
||||
resolve({ pli: report.pliCount, nack: report.nackCount });
|
||||
}
|
||||
});
|
||||
}, 250);
|
||||
});
|
||||
|
||||
videoInfoPromise.then((value) => {
|
||||
this.currentFrameRate = value.fps;
|
||||
});
|
||||
}
|
||||
|
||||
// Send binary message through the data channel
|
||||
|
||||
101
packages/play-standalone/android/.gitignore
vendored
@@ -1,101 +0,0 @@
|
||||
# Using Android gitignore template: https://github.com/github/gitignore/blob/HEAD/Android.gitignore
|
||||
|
||||
# Built application files
|
||||
*.apk
|
||||
*.aar
|
||||
*.ap_
|
||||
*.aab
|
||||
|
||||
# Files for the ART/Dalvik VM
|
||||
*.dex
|
||||
|
||||
# Java class files
|
||||
*.class
|
||||
|
||||
# Generated files
|
||||
bin/
|
||||
gen/
|
||||
out/
|
||||
# Uncomment the following line in case you need and you don't have the release build type files in your app
|
||||
# release/
|
||||
|
||||
# Gradle files
|
||||
.gradle/
|
||||
build/
|
||||
|
||||
# Local configuration file (sdk path, etc)
|
||||
local.properties
|
||||
|
||||
# Proguard folder generated by Eclipse
|
||||
proguard/
|
||||
|
||||
# Log Files
|
||||
*.log
|
||||
|
||||
# Android Studio Navigation editor temp files
|
||||
.navigation/
|
||||
|
||||
# Android Studio captures folder
|
||||
captures/
|
||||
|
||||
# IntelliJ
|
||||
*.iml
|
||||
.idea/workspace.xml
|
||||
.idea/tasks.xml
|
||||
.idea/gradle.xml
|
||||
.idea/assetWizardSettings.xml
|
||||
.idea/dictionaries
|
||||
.idea/libraries
|
||||
# Android Studio 3 in .gitignore file.
|
||||
.idea/caches
|
||||
.idea/modules.xml
|
||||
# Comment next line if keeping position of elements in Navigation Editor is relevant for you
|
||||
.idea/navEditor.xml
|
||||
|
||||
# Keystore files
|
||||
# Uncomment the following lines if you do not want to check your keystore files in.
|
||||
#*.jks
|
||||
#*.keystore
|
||||
|
||||
# External native build folder generated in Android Studio 2.2 and later
|
||||
.externalNativeBuild
|
||||
.cxx/
|
||||
|
||||
# Google Services (e.g. APIs or Firebase)
|
||||
# google-services.json
|
||||
|
||||
# Freeline
|
||||
freeline.py
|
||||
freeline/
|
||||
freeline_project_description.json
|
||||
|
||||
# fastlane
|
||||
fastlane/report.xml
|
||||
fastlane/Preview.html
|
||||
fastlane/screenshots
|
||||
fastlane/test_output
|
||||
fastlane/readme.md
|
||||
|
||||
# Version control
|
||||
vcs.xml
|
||||
|
||||
# lint
|
||||
lint/intermediates/
|
||||
lint/generated/
|
||||
lint/outputs/
|
||||
lint/tmp/
|
||||
# lint/reports/
|
||||
|
||||
# Android Profiling
|
||||
*.hprof
|
||||
|
||||
# Cordova plugins for Capacitor
|
||||
capacitor-cordova-android-plugins
|
||||
|
||||
# Copied web assets
|
||||
app/src/main/assets/public
|
||||
|
||||
# Generated Config files
|
||||
app/src/main/assets/capacitor.config.json
|
||||
app/src/main/assets/capacitor.plugins.json
|
||||
app/src/main/res/xml/config.xml
|
||||
@@ -1,2 +0,0 @@
|
||||
/build/*
|
||||
!/build/.npmkeep
|
||||
@@ -1,54 +0,0 @@
|
||||
apply plugin: 'com.android.application'
|
||||
|
||||
android {
|
||||
namespace "com.nestri.play"
|
||||
compileSdk rootProject.ext.compileSdkVersion
|
||||
defaultConfig {
|
||||
applicationId "com.nestri.play"
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
aaptOptions {
|
||||
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
|
||||
// Default: https://android.googlesource.com/platform/frameworks/base/+/282e181b58cf72b6ca770dc7ca5f91f135444502/tools/aapt/AaptAssets.cpp#61
|
||||
ignoreAssetsPattern '!.svn:!.git:!.ds_store:!*.scc:.*:!CVS:!thumbs.db:!picasa.ini:!*~'
|
||||
}
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
flatDir{
|
||||
dirs '../capacitor-cordova-android-plugins/src/main/libs', 'libs'
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(include: ['*.jar'], dir: 'libs')
|
||||
implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion"
|
||||
implementation "androidx.coordinatorlayout:coordinatorlayout:$androidxCoordinatorLayoutVersion"
|
||||
implementation "androidx.core:core-splashscreen:$coreSplashScreenVersion"
|
||||
implementation project(':capacitor-android')
|
||||
testImplementation "junit:junit:$junitVersion"
|
||||
androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion"
|
||||
androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion"
|
||||
implementation project(':capacitor-cordova-android-plugins')
|
||||
}
|
||||
|
||||
apply from: 'capacitor.build.gradle'
|
||||
|
||||
try {
|
||||
def servicesJSON = file('google-services.json')
|
||||
if (servicesJSON.text) {
|
||||
apply plugin: 'com.google.gms.google-services'
|
||||
}
|
||||
} catch(Exception e) {
|
||||
logger.info("google-services.json not found, google-services plugin not applied. Push Notifications won't work")
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
// DO NOT EDIT THIS FILE! IT IS GENERATED EACH TIME "capacitor update" IS RUN
|
||||
|
||||
android {
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_21
|
||||
targetCompatibility JavaVersion.VERSION_21
|
||||
}
|
||||
}
|
||||
|
||||
apply from: "../capacitor-cordova-android-plugins/cordova.variables.gradle"
|
||||
dependencies {
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
if (hasProperty('postBuildExtras')) {
|
||||
postBuildExtras()
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
||||
@@ -1,26 +0,0 @@
|
||||
package com.getcapacitor.myapp;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import android.content.Context;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.platform.app.InstrumentationRegistry;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
/**
|
||||
* Instrumented test, which will execute on an Android device.
|
||||
*
|
||||
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class ExampleInstrumentedTest {
|
||||
|
||||
@Test
|
||||
public void useAppContext() throws Exception {
|
||||
// Context of the app under test.
|
||||
Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
|
||||
|
||||
assertEquals("com.getcapacitor.app", appContext.getPackageName());
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/AppTheme">
|
||||
|
||||
<activity
|
||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|smallestScreenSize|screenLayout|uiMode|navigation"
|
||||
android:name=".MainActivity"
|
||||
android:label="@string/title_activity_main"
|
||||
android:theme="@style/AppTheme.NoActionBarLaunch"
|
||||
android:launchMode="singleTask"
|
||||
android:exported="true">
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
|
||||
</activity>
|
||||
|
||||
<provider
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
android:authorities="${applicationId}.fileprovider"
|
||||
android:exported="false"
|
||||
android:grantUriPermissions="true">
|
||||
<meta-data
|
||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||
android:resource="@xml/file_paths"></meta-data>
|
||||
</provider>
|
||||
</application>
|
||||
|
||||
<!-- Permissions -->
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
</manifest>
|
||||
@@ -1,5 +0,0 @@
|
||||
package com.nestri.play;
|
||||
|
||||
import com.getcapacitor.BridgeActivity;
|
||||
|
||||
public class MainActivity extends BridgeActivity {}
|
||||
|
Before Width: | Height: | Size: 7.5 KiB |
|
Before Width: | Height: | Size: 3.9 KiB |
|
Before Width: | Height: | Size: 9.0 KiB |
|
Before Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 7.7 KiB |
|
Before Width: | Height: | Size: 4.0 KiB |
|
Before Width: | Height: | Size: 9.6 KiB |
|
Before Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 17 KiB |
@@ -1,34 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportHeight="108"
|
||||
android:viewportWidth="108">
|
||||
<path
|
||||
android:fillType="evenOdd"
|
||||
android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
|
||||
android:strokeColor="#00000000"
|
||||
android:strokeWidth="1">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:endX="78.5885"
|
||||
android:endY="90.9159"
|
||||
android:startX="48.7653"
|
||||
android:startY="61.0927"
|
||||
android:type="linear">
|
||||
<item
|
||||
android:color="#44000000"
|
||||
android:offset="0.0" />
|
||||
<item
|
||||
android:color="#00000000"
|
||||
android:offset="1.0" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:fillType="nonZero"
|
||||
android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
|
||||
android:strokeColor="#00000000"
|
||||
android:strokeWidth="1" />
|
||||
</vector>
|
||||
@@ -1,170 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportHeight="108"
|
||||
android:viewportWidth="108">
|
||||
<path
|
||||
android:fillColor="#26A69A"
|
||||
android:pathData="M0,0h108v108h-108z" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M9,0L9,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,0L19,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M29,0L29,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M39,0L39,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M49,0L49,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M59,0L59,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M69,0L69,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M79,0L79,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M89,0L89,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M99,0L99,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,9L108,9"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,19L108,19"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,29L108,29"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,39L108,39"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,49L108,49"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,59L108,59"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,69L108,69"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,79L108,79"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,89L108,89"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,99L108,99"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,29L89,29"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,39L89,39"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,49L89,49"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,59L89,59"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,69L89,69"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,79L89,79"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M29,19L29,89"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M39,19L39,89"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M49,19L49,89"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M59,19L59,89"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M69,19L69,89"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M79,19L79,89"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
</vector>
|
||||
|
Before Width: | Height: | Size: 3.9 KiB |
@@ -1,12 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".MainActivity">
|
||||
|
||||
<WebView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
@@ -1,5 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
@@ -1,5 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
|
Before Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 3.9 KiB |
|
Before Width: | Height: | Size: 4.9 KiB |
|
Before Width: | Height: | Size: 6.4 KiB |
|
Before Width: | Height: | Size: 6.5 KiB |
|
Before Width: | Height: | Size: 9.6 KiB |
|
Before Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 9.2 KiB |
|
Before Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 16 KiB |
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="ic_launcher_background">#FFFFFF</color>
|
||||
</resources>
|
||||
@@ -1,7 +0,0 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<resources>
|
||||
<string name="app_name">Nestri Play</string>
|
||||
<string name="title_activity_main">Nestri Play</string>
|
||||
<string name="package_name">com.nestri.play</string>
|
||||
<string name="custom_url_scheme">com.nestri.play</string>
|
||||
</resources>
|
||||
@@ -1,22 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<!-- Base application theme. -->
|
||||
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
|
||||
<!-- Customize your theme here. -->
|
||||
<item name="colorPrimary">@color/colorPrimary</item>
|
||||
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
|
||||
<item name="colorAccent">@color/colorAccent</item>
|
||||
</style>
|
||||
|
||||
<style name="AppTheme.NoActionBar" parent="Theme.AppCompat.DayNight.NoActionBar">
|
||||
<item name="windowActionBar">false</item>
|
||||
<item name="windowNoTitle">true</item>
|
||||
<item name="android:background">@null</item>
|
||||
</style>
|
||||
|
||||
|
||||
<style name="AppTheme.NoActionBarLaunch" parent="Theme.SplashScreen">
|
||||
<item name="android:background">@drawable/splash</item>
|
||||
</style>
|
||||
</resources>
|
||||
@@ -1,5 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<paths xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<external-path name="my_images" path="." />
|
||||
<cache-path name="my_cache_images" path="." />
|
||||
</paths>
|
||||
@@ -1,18 +0,0 @@
|
||||
package com.getcapacitor.myapp;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Example local unit test, which will execute on the development machine (host).
|
||||
*
|
||||
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||
*/
|
||||
public class ExampleUnitTest {
|
||||
|
||||
@Test
|
||||
public void addition_isCorrect() throws Exception {
|
||||
assertEquals(4, 2 + 2);
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
|
||||
buildscript {
|
||||
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:8.7.2'
|
||||
classpath 'com.google.gms:google-services:4.4.2'
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
}
|
||||
}
|
||||
|
||||
apply from: "variables.gradle"
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
}
|
||||
|
||||
task clean(type: Delete) {
|
||||
delete rootProject.buildDir
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
// DO NOT EDIT THIS FILE! IT IS GENERATED EACH TIME "capacitor update" IS RUN
|
||||
include ':capacitor-android'
|
||||
project(':capacitor-android').projectDir = new File('../../../node_modules/@capacitor/android/capacitor')
|
||||
@@ -1,22 +0,0 @@
|
||||
# Project-wide Gradle settings.
|
||||
|
||||
# IDE (e.g. Android Studio) users:
|
||||
# Gradle settings configured through the IDE *will override*
|
||||
# any settings specified in this file.
|
||||
|
||||
# For more details on how to configure your build environment visit
|
||||
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
||||
|
||||
# Specifies the JVM arguments used for the daemon process.
|
||||
# The setting is particularly useful for tweaking memory settings.
|
||||
org.gradle.jvmargs=-Xmx1536m
|
||||
|
||||
# When configured, Gradle will run in incubating parallel mode.
|
||||
# This option should only be used with decoupled projects. More details, visit
|
||||
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||
# org.gradle.parallel=true
|
||||
|
||||
# AndroidX package structure to make it clearer which packages are bundled with the
|
||||
# Android operating system, and which are packaged with your app's APK
|
||||
# https://developer.android.com/topic/libraries/support-library/androidx-rn
|
||||
android.useAndroidX=true
|
||||
@@ -1,7 +0,0 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-all.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
252
packages/play-standalone/android/gradlew
vendored
@@ -1,252 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright © 2015-2021 the original authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
#
|
||||
# Gradle start up script for POSIX generated by Gradle.
|
||||
#
|
||||
# Important for running:
|
||||
#
|
||||
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||
# noncompliant, but you have some other compliant shell such as ksh or
|
||||
# bash, then to run this script, type that shell name before the whole
|
||||
# command line, like:
|
||||
#
|
||||
# ksh Gradle
|
||||
#
|
||||
# Busybox and similar reduced shells will NOT work, because this script
|
||||
# requires all of these POSIX shell features:
|
||||
# * functions;
|
||||
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||
# * compound commands having a testable exit status, especially «case»;
|
||||
# * various built-in commands including «command», «set», and «ulimit».
|
||||
#
|
||||
# Important for patching:
|
||||
#
|
||||
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||
#
|
||||
# The "traditional" practice of packing multiple parameters into a
|
||||
# space-separated string is a well documented source of bugs and security
|
||||
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||
# options in "$@", and eventually passing that to Java.
|
||||
#
|
||||
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||
# see the in-line comments for details.
|
||||
#
|
||||
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
|
||||
# Resolve links: $0 may be a link
|
||||
app_path=$0
|
||||
|
||||
# Need this for daisy-chained symlinks.
|
||||
while
|
||||
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||
[ -h "$app_path" ]
|
||||
do
|
||||
ls=$( ls -ld "$app_path" )
|
||||
link=${ls#*' -> '}
|
||||
case $link in #(
|
||||
/*) app_path=$link ;; #(
|
||||
*) app_path=$APP_HOME$link ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# This is normally unused
|
||||
# shellcheck disable=SC2034
|
||||
APP_BASE_NAME=${0##*/}
|
||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
|
||||
' "$PWD" ) || exit
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD=maximum
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
} >&2
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
} >&2
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "$( uname )" in #(
|
||||
CYGWIN* ) cygwin=true ;; #(
|
||||
Darwin* ) darwin=true ;; #(
|
||||
MSYS* | MINGW* ) msys=true ;; #(
|
||||
NONSTOP* ) nonstop=true ;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD=$JAVA_HOME/jre/sh/java
|
||||
else
|
||||
JAVACMD=$JAVA_HOME/bin/java
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD=java
|
||||
if ! command -v java >/dev/null 2>&1
|
||||
then
|
||||
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
case $MAX_FD in #(
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
fi
|
||||
|
||||
# Collect all arguments for the java command, stacking in reverse order:
|
||||
# * args from the command line
|
||||
# * the main class name
|
||||
# * -classpath
|
||||
# * -D...appname settings
|
||||
# * --module-path (only if needed)
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if "$cygwin" || "$msys" ; then
|
||||
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||
|
||||
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
for arg do
|
||||
if
|
||||
case $arg in #(
|
||||
-*) false ;; # don't mess with options #(
|
||||
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
||||
[ -e "$t" ] ;; #(
|
||||
*) false ;;
|
||||
esac
|
||||
then
|
||||
arg=$( cygpath --path --ignore --mixed "$arg" )
|
||||
fi
|
||||
# Roll the args list around exactly as many times as the number of
|
||||
# args, so each arg winds up back in the position where it started, but
|
||||
# possibly modified.
|
||||
#
|
||||
# NB: a `for` loop captures its iteration list before it begins, so
|
||||
# changing the positional parameters here affects neither the number of
|
||||
# iterations, nor the values presented in `arg`.
|
||||
shift # remove old arg
|
||||
set -- "$@" "$arg" # push replacement arg
|
||||
done
|
||||
fi
|
||||
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Collect all arguments for the java command:
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||
# and any embedded shellness will be escaped.
|
||||
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||
# treated as '${Hostname}' itself on the command line.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
-classpath "$CLASSPATH" \
|
||||
org.gradle.wrapper.GradleWrapperMain \
|
||||
"$@"
|
||||
|
||||
# Stop when "xargs" is not available.
|
||||
if ! command -v xargs >/dev/null 2>&1
|
||||
then
|
||||
die "xargs is not available"
|
||||
fi
|
||||
|
||||
# Use "xargs" to parse quoted args.
|
||||
#
|
||||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||
#
|
||||
# In Bash we could simply go:
|
||||
#
|
||||
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||
# set -- "${ARGS[@]}" "$@"
|
||||
#
|
||||
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||
# character that might be a shell metacharacter, then use eval to reverse
|
||||
# that process (while maintaining the separation between arguments), and wrap
|
||||
# the whole thing up as a single "set" statement.
|
||||
#
|
||||
# This will of course break if any of these variables contains a newline or
|
||||
# an unmatched quote.
|
||||
#
|
||||
|
||||
eval "set -- $(
|
||||
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||
xargs -n1 |
|
||||
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||
tr '\n' ' '
|
||||
)" '"$@"'
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
94
packages/play-standalone/android/gradlew.bat
vendored
@@ -1,94 +0,0 @@
|
||||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
@rem SPDX-License-Identifier: Apache-2.0
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%"=="" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%"=="" set DIRNAME=.
|
||||
@rem This is normally unused
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if %ERRORLEVEL% equ 0 goto execute
|
||||
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
set EXIT_CODE=%ERRORLEVEL%
|
||||
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||
exit /b %EXIT_CODE%
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
@@ -1,5 +0,0 @@
|
||||
include ':app'
|
||||
include ':capacitor-cordova-android-plugins'
|
||||
project(':capacitor-cordova-android-plugins').projectDir = new File('./capacitor-cordova-android-plugins/')
|
||||
|
||||
apply from: 'capacitor.settings.gradle'
|
||||
@@ -1,16 +0,0 @@
|
||||
ext {
|
||||
minSdkVersion = 23
|
||||
compileSdkVersion = 35
|
||||
targetSdkVersion = 35
|
||||
androidxActivityVersion = '1.9.2'
|
||||
androidxAppCompatVersion = '1.7.0'
|
||||
androidxCoordinatorLayoutVersion = '1.2.0'
|
||||
androidxCoreVersion = '1.15.0'
|
||||
androidxFragmentVersion = '1.8.4'
|
||||
coreSplashScreenVersion = '1.0.1'
|
||||
androidxWebkitVersion = '1.12.1'
|
||||
junitVersion = '4.13.2'
|
||||
androidxJunitVersion = '1.2.1'
|
||||
androidxEspressoCoreVersion = '3.6.1'
|
||||
cordovaAndroidVersion = '10.1.1'
|
||||
}
|
||||
@@ -2,10 +2,12 @@
|
||||
import { defineConfig, envField } from "astro/config";
|
||||
import node from "@astrojs/node";
|
||||
|
||||
|
||||
// https://astro.build/config
|
||||
export default defineConfig({
|
||||
output: "static",
|
||||
adapter: node({
|
||||
mode: 'standalone',
|
||||
}),
|
||||
output: "server",
|
||||
server: {
|
||||
"host": "0.0.0.0",
|
||||
"port": 3000,
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
import type { CapacitorConfig } from '@capacitor/cli';
|
||||
|
||||
const config: CapacitorConfig = {
|
||||
appId: 'com.nestri.play',
|
||||
appName: 'Nestri Play',
|
||||
webDir: 'dist'
|
||||
};
|
||||
|
||||
export default config;
|
||||
@@ -5,16 +5,10 @@
|
||||
"scripts": {
|
||||
"dev": "astro dev",
|
||||
"build": "astro build",
|
||||
"build:cap": "astro build && npx cap copy",
|
||||
"preview": "astro preview",
|
||||
"astro": "astro",
|
||||
"sync:android": "npm run build && npx cap sync android"
|
||||
"astro": "astro"
|
||||
},
|
||||
"dependencies": {
|
||||
"@capacitor/android": "^7.4.3",
|
||||
"@capacitor/cli": "^7.4.3",
|
||||
"@capacitor/core": "^7.4.3",
|
||||
"@capacitor/ios": "^7.4.3",
|
||||
"@astrojs/node": "9.5.0",
|
||||
"@nestri/input": "*",
|
||||
"astro": "5.15.1"
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
import DefaultLayout from "../layouts/DefaultLayout.astro";
|
||||
const { room } = Astro.params;
|
||||
|
||||
// Passing of environment variables to the client side
|
||||
// gotta love node and it's ecosystem..
|
||||
@@ -18,7 +19,7 @@ if (envs_map.size > 0) {
|
||||
<DefaultLayout>
|
||||
<h1 id="offlineText" class="offline">Offline</h1>
|
||||
<h1 id="loadingText" class="loading">Warming up the GPU...</h1>
|
||||
<canvas id="playCanvas" class="playCanvas"></canvas>
|
||||
<canvas id="playCanvas" class="playCanvas" data-room={room}></canvas>
|
||||
<div id="ENVS" data-envs={envs}></div>
|
||||
</DefaultLayout>
|
||||
|
||||
@@ -41,9 +42,9 @@ if (envs_map.size > 0) {
|
||||
const offlineText = document.getElementById("offlineText")! as HTMLHeadingElement;
|
||||
const loadingText = document.getElementById("loadingText")! as HTMLHeadingElement;
|
||||
|
||||
const room = window.location.hash.substring(1);
|
||||
const room = canvas.dataset.room;
|
||||
if (!room || room.length <= 0) {
|
||||
throw new Error("Room parameter is required in URL hash");
|
||||
throw new Error("Room parameter is required");
|
||||
}
|
||||
|
||||
offlineText.style.display = "flex";
|
||||
@@ -98,9 +99,6 @@ if (envs_map.size > 0) {
|
||||
});
|
||||
window.addEventListener("gamepaddisconnected", (e) => {
|
||||
console.log("Gamepad disconnected:", e.gamepad);
|
||||
if (e.gamepad.id.toLowerCase().includes("nestri"))
|
||||
return;
|
||||
|
||||
let disconnected = nestriControllers.find((c) => c.getSlot() === e.gamepad.index);
|
||||
if (disconnected) {
|
||||
disconnected.dispose();
|
||||
@@ -1,138 +0,0 @@
|
||||
---
|
||||
import { ClientRouter } from "astro:transitions";
|
||||
import { navigate } from "astro:transitions/client";
|
||||
---
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<title>Nestri Play</title>
|
||||
<ClientRouter />
|
||||
<style>
|
||||
body {
|
||||
font-family: sans-serif;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100vh;
|
||||
margin: 0;
|
||||
}
|
||||
form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
padding: 2rem;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 8px;
|
||||
}
|
||||
div {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
label {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
input {
|
||||
padding: 0.5rem;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
}
|
||||
button {
|
||||
padding: 0.75rem;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
background-color: #007bff;
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
}
|
||||
button:hover {
|
||||
background-color: #0056b3;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<form id="join-form">
|
||||
<h1>Nestri Play</h1>
|
||||
<div>
|
||||
<label for="room">Room</label>
|
||||
<input type="text" id="room" name="room" required list="room-list">
|
||||
<datalist id="room-list"></datalist>
|
||||
</div>
|
||||
<div>
|
||||
<label for="peerURL">peerURL</label>
|
||||
<input type="text" id="peerURL" name="peerURL" required list="peerURL-list">
|
||||
<datalist id="peerURL-list"></datalist>
|
||||
</div>
|
||||
<button type="submit">Join</button>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
import { navigate } from "astro:transitions/client";
|
||||
|
||||
const roomInput = document.getElementById('room') as HTMLInputElement;
|
||||
const peerURLInput = document.getElementById('peerURL') as HTMLInputElement;
|
||||
const roomList = document.getElementById('room-list');
|
||||
const peerURLList = document.getElementById('peerURL-list');
|
||||
|
||||
// Load values from cookies
|
||||
function getCookie(name) {
|
||||
const value = `; ${document.cookie}`;
|
||||
const parts = value.split(`; ${name}=`);
|
||||
if (parts.length === 2) {
|
||||
const cookieValue = parts.pop()?.split(';').shift();
|
||||
return cookieValue ? decodeURIComponent(cookieValue) : undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function setCookie(name, value, days) {
|
||||
const d = new Date();
|
||||
d.setTime(d.getTime() + (days*24*60*60*1000));
|
||||
const expires = "expires="+ d.toUTCString();
|
||||
document.cookie = name + "=" + encodeURIComponent(value) + ";" + expires + ";path=/";
|
||||
}
|
||||
|
||||
const storedRooms = JSON.parse(getCookie('nestri-rooms') || '[]');
|
||||
const storedPeerURLs = JSON.parse(getCookie('nestri-peerURLs') || '[]');
|
||||
|
||||
if (roomList) {
|
||||
storedRooms.forEach(room => {
|
||||
const option = document.createElement('option');
|
||||
option.value = room;
|
||||
roomList.appendChild(option);
|
||||
});
|
||||
}
|
||||
|
||||
if (peerURLList) {
|
||||
storedPeerURLs.forEach(peerURL => {
|
||||
const option = document.createElement('option');
|
||||
option.value = peerURL;
|
||||
peerURLList.appendChild(option);
|
||||
});
|
||||
}
|
||||
|
||||
if (storedRooms.length > 0 && roomInput) {
|
||||
roomInput.value = storedRooms[0];
|
||||
}
|
||||
if (storedPeerURLs.length > 0 && peerURLInput) {
|
||||
peerURLInput.value = storedPeerURLs[0];
|
||||
}
|
||||
|
||||
document.getElementById('join-form')?.addEventListener('submit', function(event) {
|
||||
event.preventDefault();
|
||||
const room = roomInput.value;
|
||||
const peerURL = peerURLInput.value;
|
||||
|
||||
// Save values to cookies
|
||||
const newRooms = [room, ...storedRooms.filter(r => r !== room)].slice(0, 10);
|
||||
const newPeerURLs = [peerURL, ...storedPeerURLs.filter(p => p !== peerURL)].slice(0, 10);
|
||||
|
||||
setCookie('nestri-rooms', JSON.stringify(newRooms), 365);
|
||||
setCookie('nestri-peerURLs', JSON.stringify(newPeerURLs), 365);
|
||||
|
||||
if (room && peerURL) {
|
||||
navigate(`/play/index.html?peerURL=${encodeURIComponent(peerURL)}#${room}`);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -2,6 +2,7 @@ package common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/pion/interceptor/pkg/nack"
|
||||
"log/slog"
|
||||
"strconv"
|
||||
|
||||
@@ -30,20 +31,121 @@ func InitWebRTCAPI() error {
|
||||
return fmt.Errorf("failed to register extensions: %w", err)
|
||||
}
|
||||
|
||||
// Default codecs cover our needs
|
||||
err = mediaEngine.RegisterDefaultCodecs()
|
||||
if err != nil {
|
||||
// Register codecs
|
||||
for _, codec := range []webrtc.RTPCodecParameters{
|
||||
{
|
||||
RTPCodecCapability: webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeOpus, ClockRate: 48000, Channels: 2, SDPFmtpLine: "minptime=10;useinbandfec=1"},
|
||||
PayloadType: 111,
|
||||
},
|
||||
} {
|
||||
if err = mediaEngine.RegisterCodec(codec, webrtc.RTPCodecTypeAudio); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
videoRTCPFeedback := []webrtc.RTCPFeedback{{"nack", ""}, {"nack", "pli"}}
|
||||
for _, codec := range []webrtc.RTPCodecParameters{
|
||||
{
|
||||
RTPCodecCapability: webrtc.RTPCodecCapability{
|
||||
MimeType: webrtc.MimeTypeH264, ClockRate: 90000,
|
||||
SDPFmtpLine: "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f",
|
||||
RTCPFeedback: videoRTCPFeedback,
|
||||
},
|
||||
PayloadType: 102,
|
||||
},
|
||||
{
|
||||
RTPCodecCapability: webrtc.RTPCodecCapability{
|
||||
MimeType: webrtc.MimeTypeH264, ClockRate: 90000,
|
||||
SDPFmtpLine: "level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42001f",
|
||||
RTCPFeedback: videoRTCPFeedback,
|
||||
},
|
||||
PayloadType: 104,
|
||||
},
|
||||
{
|
||||
RTPCodecCapability: webrtc.RTPCodecCapability{
|
||||
MimeType: webrtc.MimeTypeH264, ClockRate: 90000,
|
||||
SDPFmtpLine: "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f",
|
||||
RTCPFeedback: videoRTCPFeedback,
|
||||
},
|
||||
PayloadType: 106,
|
||||
},
|
||||
{
|
||||
RTPCodecCapability: webrtc.RTPCodecCapability{
|
||||
MimeType: webrtc.MimeTypeH264, ClockRate: 90000,
|
||||
SDPFmtpLine: "level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42e01f",
|
||||
RTCPFeedback: videoRTCPFeedback,
|
||||
},
|
||||
PayloadType: 108,
|
||||
},
|
||||
{
|
||||
RTPCodecCapability: webrtc.RTPCodecCapability{
|
||||
MimeType: webrtc.MimeTypeH264, ClockRate: 90000,
|
||||
SDPFmtpLine: "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=4d001f",
|
||||
RTCPFeedback: videoRTCPFeedback,
|
||||
},
|
||||
PayloadType: 127,
|
||||
},
|
||||
{
|
||||
RTPCodecCapability: webrtc.RTPCodecCapability{
|
||||
MimeType: webrtc.MimeTypeH264,
|
||||
ClockRate: 90000,
|
||||
SDPFmtpLine: "level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=4d001f",
|
||||
RTCPFeedback: videoRTCPFeedback,
|
||||
},
|
||||
PayloadType: 39,
|
||||
},
|
||||
{
|
||||
RTPCodecCapability: webrtc.RTPCodecCapability{
|
||||
MimeType: webrtc.MimeTypeH265,
|
||||
ClockRate: 90000,
|
||||
RTCPFeedback: videoRTCPFeedback,
|
||||
},
|
||||
PayloadType: 116,
|
||||
},
|
||||
{
|
||||
RTPCodecCapability: webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeAV1, ClockRate: 90000, RTCPFeedback: videoRTCPFeedback},
|
||||
PayloadType: 45,
|
||||
},
|
||||
{
|
||||
RTPCodecCapability: webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeVP9, ClockRate: 90000, SDPFmtpLine: "profile-id=0", RTCPFeedback: videoRTCPFeedback},
|
||||
PayloadType: 98,
|
||||
},
|
||||
{
|
||||
RTPCodecCapability: webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeVP9, ClockRate: 90000, SDPFmtpLine: "profile-id=2", RTCPFeedback: videoRTCPFeedback},
|
||||
PayloadType: 100,
|
||||
},
|
||||
{
|
||||
RTPCodecCapability: webrtc.RTPCodecCapability{
|
||||
MimeType: webrtc.MimeTypeH264, ClockRate: 90000,
|
||||
SDPFmtpLine: "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=64001f",
|
||||
RTCPFeedback: videoRTCPFeedback,
|
||||
},
|
||||
PayloadType: 112,
|
||||
},
|
||||
} {
|
||||
if err = mediaEngine.RegisterCodec(codec, webrtc.RTPCodecTypeVideo); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Interceptor registry
|
||||
interceptorRegistry := &interceptor.Registry{}
|
||||
|
||||
// Use default set
|
||||
err = webrtc.RegisterDefaultInterceptors(mediaEngine, interceptorRegistry)
|
||||
// Register our interceptors..
|
||||
nackGenFactory, err := nack.NewGeneratorInterceptor()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
interceptorRegistry.Add(nackGenFactory)
|
||||
nackRespFactory, err := nack.NewResponderInterceptor()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
interceptorRegistry.Add(nackRespFactory)
|
||||
|
||||
if err = webrtc.ConfigureRTCPReports(interceptorRegistry); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Setting engine
|
||||
settingEngine := webrtc.SettingEngine{}
|
||||
@@ -53,7 +155,7 @@ func InitWebRTCAPI() error {
|
||||
|
||||
nat11IP := GetFlags().NAT11IP
|
||||
if len(nat11IP) > 0 {
|
||||
settingEngine.SetNAT1To1IPs([]string{nat11IP}, webrtc.ICECandidateTypeSrflx)
|
||||
settingEngine.SetNAT1To1IPs([]string{nat11IP}, webrtc.ICECandidateTypeHost)
|
||||
slog.Info("Using NAT 1:1 IP for WebRTC", "nat11_ip", nat11IP)
|
||||
}
|
||||
|
||||
|
||||
@@ -125,7 +125,7 @@ func getLocalIP() string {
|
||||
return ""
|
||||
}
|
||||
for _, address := range addrs {
|
||||
if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
|
||||
if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() && !ipnet.IP.IsPrivate() && !ipnet.IP.IsUnspecified() {
|
||||
if ipnet.IP.To4() != nil || ipnet.IP != nil {
|
||||
return ipnet.IP.String()
|
||||
}
|
||||
|
||||
@@ -20,9 +20,9 @@ import (
|
||||
rcmgr "github.com/libp2p/go-libp2p/p2p/host/resource-manager"
|
||||
"github.com/libp2p/go-libp2p/p2p/protocol/ping"
|
||||
"github.com/libp2p/go-libp2p/p2p/security/noise"
|
||||
p2pquic "github.com/libp2p/go-libp2p/p2p/transport/quic"
|
||||
"github.com/libp2p/go-libp2p/p2p/transport/quicreuse"
|
||||
"github.com/libp2p/go-libp2p/p2p/transport/tcp"
|
||||
ws "github.com/libp2p/go-libp2p/p2p/transport/websocket"
|
||||
webtransport "github.com/libp2p/go-libp2p/p2p/transport/webtransport"
|
||||
"github.com/multiformats/go-multiaddr"
|
||||
"github.com/oklog/ulid/v2"
|
||||
@@ -91,10 +91,10 @@ func NewRelay(ctx context.Context, port int, identityKey crypto.PrivKey) (*Relay
|
||||
listenAddrs := []string{
|
||||
fmt.Sprintf("/ip4/0.0.0.0/tcp/%d", port), // IPv4 - Raw TCP
|
||||
fmt.Sprintf("/ip6/::/tcp/%d", port), // IPv6 - Raw TCP
|
||||
fmt.Sprintf("/ip4/0.0.0.0/tcp/%d/ws", port), // IPv4 - TCP WebSocket
|
||||
fmt.Sprintf("/ip6/::/tcp/%d/ws", port), // IPv6 - TCP WebSocket
|
||||
fmt.Sprintf("/ip4/0.0.0.0/udp/%d/quic-v1/webtransport", port), // IPv4 - UDP QUIC WebTransport
|
||||
fmt.Sprintf("/ip6/::/udp/%d/quic-v1/webtransport", port), // IPv6 - UDP QUIC WebTransport
|
||||
fmt.Sprintf("/ip4/0.0.0.0/udp/%d/quic-v1", port), // IPv4 - UDP Raw QUIC
|
||||
fmt.Sprintf("/ip6/::/udp/%d/quic-v1", port), // IPv6 - UDP Raw QUIC
|
||||
}
|
||||
|
||||
var muAddrs []multiaddr.Multiaddr
|
||||
@@ -112,8 +112,8 @@ func NewRelay(ctx context.Context, port int, identityKey crypto.PrivKey) (*Relay
|
||||
libp2p.Identity(identityKey),
|
||||
// Enable required transports
|
||||
libp2p.Transport(tcp.NewTCPTransport),
|
||||
libp2p.Transport(ws.New),
|
||||
libp2p.Transport(webtransport.New),
|
||||
libp2p.Transport(p2pquic.NewTransport),
|
||||
// Other options
|
||||
libp2p.ListenAddrs(muAddrs...),
|
||||
libp2p.Security(noise.ID, noise.New),
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"log/slog"
|
||||
"math"
|
||||
"relay/internal/common"
|
||||
"relay/internal/connections"
|
||||
"relay/internal/shared"
|
||||
@@ -606,52 +605,8 @@ func (sp *StreamProtocol) handleStreamPush(stream network.Stream) {
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate differences
|
||||
var timeDiff int64
|
||||
var sequenceDiff int
|
||||
|
||||
if remoteTrack.Kind() == webrtc.RTPCodecTypeVideo {
|
||||
timeDiff = int64(rtpPacket.Timestamp) - int64(room.LastVideoTimestamp)
|
||||
if !room.VideoTimestampSet {
|
||||
timeDiff = 0
|
||||
room.VideoTimestampSet = true
|
||||
} else if timeDiff < -(math.MaxUint32 / 10) {
|
||||
timeDiff += math.MaxUint32 + 1
|
||||
}
|
||||
|
||||
sequenceDiff = int(rtpPacket.SequenceNumber) - int(room.LastVideoSequenceNumber)
|
||||
if !room.VideoSequenceSet {
|
||||
sequenceDiff = 0
|
||||
room.VideoSequenceSet = true
|
||||
} else if sequenceDiff < -(math.MaxUint16 / 10) {
|
||||
sequenceDiff += math.MaxUint16 + 1
|
||||
}
|
||||
|
||||
room.LastVideoTimestamp = rtpPacket.Timestamp
|
||||
room.LastVideoSequenceNumber = rtpPacket.SequenceNumber
|
||||
} else { // Audio
|
||||
timeDiff = int64(rtpPacket.Timestamp) - int64(room.LastAudioTimestamp)
|
||||
if !room.AudioTimestampSet {
|
||||
timeDiff = 0
|
||||
room.AudioTimestampSet = true
|
||||
} else if timeDiff < -(math.MaxUint32 / 10) {
|
||||
timeDiff += math.MaxUint32 + 1
|
||||
}
|
||||
|
||||
sequenceDiff = int(rtpPacket.SequenceNumber) - int(room.LastAudioSequenceNumber)
|
||||
if !room.AudioSequenceSet {
|
||||
sequenceDiff = 0
|
||||
room.AudioSequenceSet = true
|
||||
} else if sequenceDiff < -(math.MaxUint16 / 10) {
|
||||
sequenceDiff += math.MaxUint16 + 1
|
||||
}
|
||||
|
||||
room.LastAudioTimestamp = rtpPacket.Timestamp
|
||||
room.LastAudioSequenceNumber = rtpPacket.SequenceNumber
|
||||
}
|
||||
|
||||
// Broadcast with differences
|
||||
room.BroadcastPacketRetimed(remoteTrack.Kind(), rtpPacket, timeDiff, sequenceDiff)
|
||||
// Broadcast
|
||||
room.BroadcastPacket(remoteTrack.Kind(), rtpPacket)
|
||||
}
|
||||
|
||||
slog.Debug("Track closed for room", "room", room.Name, "track_kind", remoteTrack.Kind().String())
|
||||
|
||||
@@ -106,28 +106,15 @@ func (p *Participant) Close() {
|
||||
func (p *Participant) packetWriter() {
|
||||
for pkt := range p.packetQueue {
|
||||
var track *webrtc.TrackLocalStaticRTP
|
||||
var sequenceNumber uint16
|
||||
var timestamp uint32
|
||||
|
||||
// No mutex needed - only this goroutine modifies these
|
||||
if pkt.kind == webrtc.RTPCodecTypeAudio {
|
||||
track = p.AudioTrack
|
||||
p.AudioSequenceNumber = uint16(int(p.AudioSequenceNumber) + pkt.sequenceDiff)
|
||||
p.AudioTimestamp = uint32(int64(p.AudioTimestamp) + pkt.timeDiff)
|
||||
sequenceNumber = p.AudioSequenceNumber
|
||||
timestamp = p.AudioTimestamp
|
||||
} else {
|
||||
track = p.VideoTrack
|
||||
p.VideoSequenceNumber = uint16(int(p.VideoSequenceNumber) + pkt.sequenceDiff)
|
||||
p.VideoTimestamp = uint32(int64(p.VideoTimestamp) + pkt.timeDiff)
|
||||
sequenceNumber = p.VideoSequenceNumber
|
||||
timestamp = p.VideoTimestamp
|
||||
}
|
||||
|
||||
if track != nil {
|
||||
pkt.packet.SequenceNumber = sequenceNumber
|
||||
pkt.packet.Timestamp = timestamp
|
||||
|
||||
if err := track.WriteRTP(pkt.packet); err != nil && !errors.Is(err, io.ErrClosedPipe) {
|
||||
slog.Error("WriteRTP failed", "participant", p.ID, "kind", pkt.kind, "err", err)
|
||||
}
|
||||
|
||||
@@ -21,8 +21,6 @@ var participantPacketPool = sync.Pool{
|
||||
type participantPacket struct {
|
||||
kind webrtc.RTPCodecType
|
||||
packet *rtp.Packet
|
||||
timeDiff int64
|
||||
sequenceDiff int
|
||||
}
|
||||
|
||||
type RoomInfo struct {
|
||||
@@ -141,7 +139,7 @@ func (r *Room) IsOnline() bool {
|
||||
return r.PeerConnection != nil
|
||||
}
|
||||
|
||||
func (r *Room) BroadcastPacketRetimed(kind webrtc.RTPCodecType, pkt *rtp.Packet, timeDiff int64, sequenceDiff int) {
|
||||
func (r *Room) BroadcastPacket(kind webrtc.RTPCodecType, pkt *rtp.Packet) {
|
||||
// Lock-free load of channel slice
|
||||
channels := r.participantChannels.Load()
|
||||
|
||||
@@ -155,9 +153,7 @@ func (r *Room) BroadcastPacketRetimed(kind webrtc.RTPCodecType, pkt *rtp.Packet,
|
||||
// Get packet struct from pool
|
||||
pp := participantPacketPool.Get().(*participantPacket)
|
||||
pp.kind = kind
|
||||
pp.packet = pkt.Clone()
|
||||
pp.timeDiff = timeDiff
|
||||
pp.sequenceDiff = sequenceDiff
|
||||
pp.packet = pkt
|
||||
|
||||
select {
|
||||
case ch <- pp:
|
||||
|
||||
@@ -348,18 +348,8 @@ main() {
|
||||
setup_namespaceless
|
||||
fi
|
||||
|
||||
# Make sure /run/udev/ directory exists with /run/udev/control, needed for virtual controller support
|
||||
if [[ ! -d "/run/udev" || ! -e "/run/udev/control" ]]; then
|
||||
log "Creating /run/udev directory and control file.."
|
||||
$ENTCMD_PREFIX mkdir -p /run/udev || {
|
||||
log "Error: Failed to create /run/udev directory"
|
||||
exit 1
|
||||
}
|
||||
$ENTCMD_PREFIX touch /run/udev/control || {
|
||||
log "Error: Failed to create /run/udev/control file"
|
||||
exit 1
|
||||
}
|
||||
fi
|
||||
# Wait for vimputti socket before switching to application startup
|
||||
wait_for_socket "/tmp/vimputti-0" "vimputti" || exit 1
|
||||
|
||||
# Switch to nestri runner entrypoint
|
||||
log "Switching to application startup entrypoint..."
|
||||
|
||||
@@ -106,12 +106,9 @@ start_compositor() {
|
||||
kill_if_running "${COMPOSITOR_PID:-}" "compositor"
|
||||
kill_if_running "${APP_PID:-}" "application"
|
||||
|
||||
# Set default values only if variables are unset (not empty)
|
||||
if [[ -z "${NESTRI_LAUNCH_CMD+x}" ]]; then
|
||||
NESTRI_LAUNCH_CMD="dbus-launch steam -tenfoot -cef-force-gpu"
|
||||
fi
|
||||
# Set default compositor if unset
|
||||
if [[ -z "${NESTRI_LAUNCH_COMPOSITOR+x}" ]]; then
|
||||
NESTRI_LAUNCH_COMPOSITOR="gamescope --backend wayland --force-grab-cursor -g -f --rt --mangoapp -W ${WIDTH} -H ${HEIGHT} -r ${FRAMERATE:-60}"
|
||||
NESTRI_LAUNCH_COMPOSITOR="gamescope --backend wayland -g -f --rt -W ${WIDTH} -H ${HEIGHT} -r ${FRAMERATE:-60}"
|
||||
fi
|
||||
|
||||
# If PRELOAD_SHIM_arch's are set and exist, set LD_PRELOAD for 32/64-bit apps
|
||||
@@ -121,6 +118,16 @@ start_compositor() {
|
||||
log "Using LD_PRELOAD shim(s)"
|
||||
fi
|
||||
|
||||
# Configure launch cmd with dbus if set
|
||||
local launch_cmd=""
|
||||
if [[ -n "${NESTRI_LAUNCH_CMD+x}" ]]; then
|
||||
if $do_ld_preload; then
|
||||
launch_cmd="LD_PRELOAD='/usr/\$LIB/libvimputti_shim.so' dbus-launch $NESTRI_LAUNCH_CMD"
|
||||
else
|
||||
launch_cmd="dbus-launch $NESTRI_LAUNCH_CMD"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Launch compositor if configured
|
||||
if [[ -n "${NESTRI_LAUNCH_COMPOSITOR}" ]]; then
|
||||
local compositor_cmd="$NESTRI_LAUNCH_COMPOSITOR"
|
||||
@@ -129,17 +136,12 @@ start_compositor() {
|
||||
# Check if this is a gamescope command
|
||||
if [[ "$compositor_cmd" == *"gamescope"* ]]; then
|
||||
is_gamescope=true
|
||||
if [[ -n "$NESTRI_LAUNCH_CMD" ]] && [[ "$compositor_cmd" != *" -- "* ]]; then
|
||||
# If steam in launch command, enable gamescope integration via -e
|
||||
if [[ "$NESTRI_LAUNCH_CMD" == *"steam"* ]]; then
|
||||
compositor_cmd+=" -e"
|
||||
fi
|
||||
# If ld_preload is true, add env with LD_PRELOAD
|
||||
if $do_ld_preload; then
|
||||
compositor_cmd+=" -- env LD_PRELOAD='/usr/\$LIB/libvimputti_shim.so' bash -c $(printf %q "$NESTRI_LAUNCH_CMD")"
|
||||
else
|
||||
compositor_cmd+=" -- bash -c $(printf %q "$NESTRI_LAUNCH_CMD")"
|
||||
if [[ -n "$launch_cmd" ]] && [[ "$compositor_cmd" != *" -- "* ]]; then
|
||||
# If steam in launch command, enable gamescope integration via -e and enable mangohud
|
||||
if [[ "$launch_cmd" == *"steam"* ]]; then
|
||||
compositor_cmd+=" --mangoapp -e"
|
||||
fi
|
||||
compositor_cmd+=" -- bash -c $(printf %q "$launch_cmd")"
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -185,9 +187,9 @@ start_compositor() {
|
||||
WAYLAND_DISPLAY=wayland-0 wlr-randr --output "$OUTPUT_NAME" --custom-mode "$WIDTH"x"$HEIGHT"
|
||||
log "Patched resolution with wlr-randr"
|
||||
|
||||
if [[ -n "${NESTRI_LAUNCH_CMD}" ]]; then
|
||||
log "Starting application: $NESTRI_LAUNCH_CMD"
|
||||
WAYLAND_DISPLAY="$COMPOSITOR_SOCKET" /bin/bash -c "$NESTRI_LAUNCH_CMD" &
|
||||
if [[ -n "$launch_cmd" ]]; then
|
||||
log "Starting application: $launch_cmd"
|
||||
WAYLAND_DISPLAY="$COMPOSITOR_SOCKET" bash -c "$launch_cmd" &
|
||||
APP_PID=$!
|
||||
fi
|
||||
else
|
||||
@@ -200,9 +202,9 @@ start_compositor() {
|
||||
log "Warning: Compositor socket not found after 15 seconds ($COMPOSITOR_SOCKET)"
|
||||
else
|
||||
# Launch standalone application if no compositor
|
||||
if [[ -n "${NESTRI_LAUNCH_CMD}" ]]; then
|
||||
log "Starting application: $NESTRI_LAUNCH_CMD"
|
||||
WAYLAND_DISPLAY=wayland-1 /bin/bash -c "$NESTRI_LAUNCH_CMD" &
|
||||
if [[ -n "$launch_cmd" ]]; then
|
||||
log "Starting standalone application: $launch_cmd"
|
||||
WAYLAND_DISPLAY=wayland-1 bash -c "$launch_cmd" &
|
||||
APP_PID=$!
|
||||
else
|
||||
log "No compositor or application configured"
|
||||
|
||||
@@ -4,8 +4,6 @@ export USER=${NESTRI_USER}
|
||||
export LANG=${NESTRI_LANG}
|
||||
export HOME=${NESTRI_HOME}
|
||||
export XDG_RUNTIME_DIR=${NESTRI_XDG_RUNTIME_DIR}
|
||||
export XDG_SESSION_TYPE=x11
|
||||
export DISPLAY=:0
|
||||
|
||||
# Causes some setups to break
|
||||
export PROTON_NO_FSYNC=1
|
||||
@@ -13,3 +11,6 @@ export PROTON_NO_FSYNC=1
|
||||
# Make gstreamer GL elements work without display output (NVIDIA issue..)
|
||||
export GST_GL_API=gles2
|
||||
export GST_GL_WINDOW=surfaceless
|
||||
|
||||
# Gamescope does not respect MangoHud default config location
|
||||
export MANGOHUD_CONFIGFILE=/etc/nestri/configs/MangoHud/MangoHud.conf
|
||||
|
||||
@@ -18,7 +18,7 @@ autorestart=true
|
||||
autostart=true
|
||||
startretries=3
|
||||
priority=3
|
||||
nice=-10
|
||||
nice=-2
|
||||
environment=HOME=%(ENV_NESTRI_HOME)s,XDG_RUNTIME_DIR=%(ENV_NESTRI_XDG_RUNTIME_DIR)s
|
||||
|
||||
[program:pipewire-pulse]
|
||||
@@ -28,7 +28,7 @@ autorestart=true
|
||||
autostart=true
|
||||
startretries=3
|
||||
priority=4
|
||||
nice=-10
|
||||
nice=-2
|
||||
environment=HOME=%(ENV_NESTRI_HOME)s,XDG_RUNTIME_DIR=%(ENV_NESTRI_XDG_RUNTIME_DIR)s
|
||||
|
||||
[program:wireplumber]
|
||||
@@ -38,7 +38,7 @@ autorestart=true
|
||||
autostart=true
|
||||
startretries=3
|
||||
priority=5
|
||||
nice=-10
|
||||
nice=-2
|
||||
environment=HOME=%(ENV_NESTRI_HOME)s,XDG_RUNTIME_DIR=%(ENV_NESTRI_XDG_RUNTIME_DIR)s
|
||||
|
||||
[program:vimputti-manager]
|
||||
|
||||
739
packages/server/Cargo.lock
generated
@@ -28,7 +28,7 @@ prost = "0.14"
|
||||
prost-types = "0.14"
|
||||
parking_lot = "0.12"
|
||||
byteorder = "1.5"
|
||||
libp2p = { version = "0.56", features = ["identify", "dns", "tcp", "noise", "ping", "tokio", "serde", "yamux", "macros", "websocket", "autonat"] }
|
||||
libp2p = { version = "0.56", features = ["identify", "dns", "tcp", "noise", "ping", "tokio", "serde", "yamux", "macros", "autonat", "quic"] }
|
||||
libp2p-identify = "0.47"
|
||||
libp2p-ping = "0.47"
|
||||
libp2p-autonat = { version = "0.15", features = ["v2"] }
|
||||
@@ -37,7 +37,7 @@ libp2p-yamux = "0.47"
|
||||
libp2p-noise = "0.46"
|
||||
libp2p-dns = { version = "0.44", features = ["tokio"] }
|
||||
libp2p-tcp = { version = "0.44", features = ["tokio"] }
|
||||
libp2p-websocket = "0.45"
|
||||
libp2p-quic = { version = "0.13", features = ["tokio"] }
|
||||
dashmap = "6.1"
|
||||
anyhow = "1.0"
|
||||
unsigned-varint = "0.8"
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
[toolchain]
|
||||
channel = "1.90"
|
||||
channel = "1.91"
|
||||
@@ -122,6 +122,14 @@ impl Args {
|
||||
.value_parser(value_parser!(encoding_args::RateControlMethod))
|
||||
.default_value("cbr"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("video-latency-control")
|
||||
.long("video-latency-control")
|
||||
.env("VIDEO_LATENCY_CONTROL")
|
||||
.help("Video latency control")
|
||||
.value_parser(value_parser!(encoding_args::LatencyControl))
|
||||
.default_value("lowest-latency"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("video-cqp")
|
||||
.long("video-cqp")
|
||||
@@ -165,6 +173,14 @@ impl Args {
|
||||
)
|
||||
.default_value("8"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("keyframe-dist-secs")
|
||||
.long("keyframe-dist-secs")
|
||||
.env("KEYFRAME_DIST_SECS")
|
||||
.help("Distance between keyframes in seconds")
|
||||
.value_parser(value_parser!(u32).range(1..))
|
||||
.default_value("1"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("audio-capture-method")
|
||||
.long("audio-capture-method")
|
||||
@@ -195,6 +211,14 @@ impl Args {
|
||||
.value_parser(value_parser!(encoding_args::RateControlMethod))
|
||||
.default_value("cbr"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("audio-latency-control")
|
||||
.long("audio-latency-control")
|
||||
.env("AUDIO_LATENCY_CONTROL")
|
||||
.help("Audio latency control")
|
||||
.value_parser(value_parser!(encoding_args::LatencyControl))
|
||||
.default_value("lowest-latency"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("audio-bitrate")
|
||||
.long("audio-bitrate")
|
||||
|
||||
@@ -60,6 +60,12 @@ pub enum RateControl {
|
||||
CBR(RateControlCBR),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, ValueEnum)]
|
||||
pub enum LatencyControl {
|
||||
LowestLatency,
|
||||
HighestQuality,
|
||||
}
|
||||
|
||||
pub struct EncodingOptionsBase {
|
||||
/// Codec (e.g. "h264", "opus" etc.)
|
||||
pub codec: Codec,
|
||||
@@ -67,6 +73,8 @@ pub struct EncodingOptionsBase {
|
||||
pub encoder: Option<String>,
|
||||
/// Rate control method (e.g. "cqp", "vbr", "cbr")
|
||||
pub rate_control: RateControl,
|
||||
/// Latency control option, what to tweak settings towards (latency or quality)
|
||||
pub latency_control: LatencyControl,
|
||||
}
|
||||
impl EncodingOptionsBase {
|
||||
pub fn debug_print(&self) {
|
||||
@@ -87,6 +95,14 @@ impl EncodingOptionsBase {
|
||||
tracing::info!("-> Target Bitrate: {}", cbr.target_bitrate);
|
||||
}
|
||||
}
|
||||
match &self.latency_control {
|
||||
LatencyControl::LowestLatency => {
|
||||
tracing::info!("> Latency Control: Priorizing lowest latency");
|
||||
}
|
||||
LatencyControl::HighestQuality => {
|
||||
tracing::info!("> Latency Control: Priorizing quality at the cost of latency");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,6 +110,7 @@ pub struct VideoEncodingOptions {
|
||||
pub base: EncodingOptionsBase,
|
||||
pub encoder_type: EncoderType,
|
||||
pub bit_depth: u32,
|
||||
pub keyframe_dist_secs: u32,
|
||||
}
|
||||
impl VideoEncodingOptions {
|
||||
pub fn from_matches(matches: &clap::ArgMatches) -> Self {
|
||||
@@ -125,6 +142,10 @@ impl VideoEncodingOptions {
|
||||
max_bitrate: matches.get_one::<u32>("video-bitrate-max").unwrap().clone(),
|
||||
}),
|
||||
},
|
||||
latency_control: matches
|
||||
.get_one::<LatencyControl>("video-latency-control")
|
||||
.unwrap_or(&LatencyControl::LowestLatency)
|
||||
.clone(),
|
||||
},
|
||||
encoder_type: matches
|
||||
.get_one::<EncoderType>("video-encoder-type")
|
||||
@@ -134,6 +155,10 @@ impl VideoEncodingOptions {
|
||||
.get_one::<u32>("video-bit-depth")
|
||||
.copied()
|
||||
.unwrap_or(8),
|
||||
keyframe_dist_secs: matches
|
||||
.get_one::<u32>("keyframe-dist-secs")
|
||||
.copied()
|
||||
.unwrap_or(1),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,6 +167,7 @@ impl VideoEncodingOptions {
|
||||
self.base.debug_print();
|
||||
tracing::info!("> Encoder Type: {}", self.encoder_type.as_str());
|
||||
tracing::info!("> Bit Depth: {}", self.bit_depth);
|
||||
tracing::info!("> Keyframe Distance Seconds: {}", self.keyframe_dist_secs);
|
||||
}
|
||||
}
|
||||
impl Deref for VideoEncodingOptions {
|
||||
@@ -208,6 +234,10 @@ impl AudioEncodingOptions {
|
||||
}),
|
||||
wot => panic!("Invalid rate control method for audio: {}", wot.as_str()),
|
||||
},
|
||||
latency_control: matches
|
||||
.get_one::<LatencyControl>("audio-latency-control")
|
||||
.unwrap_or(&LatencyControl::LowestLatency)
|
||||
.clone(),
|
||||
},
|
||||
capture_method: matches
|
||||
.get_one::<AudioCaptureMethod>("audio-capture-method")
|
||||
|
||||
@@ -74,7 +74,6 @@ pub enum EncoderAPI {
|
||||
QSV,
|
||||
VAAPI,
|
||||
NVENC,
|
||||
AMF,
|
||||
SOFTWARE,
|
||||
UNKNOWN,
|
||||
}
|
||||
@@ -85,7 +84,6 @@ impl EncoderAPI {
|
||||
Self::QSV => "Intel QuickSync Video",
|
||||
Self::VAAPI => "Video Acceleration API",
|
||||
Self::NVENC => "NVIDIA NVENC",
|
||||
Self::AMF => "AMD Media Framework",
|
||||
Self::SOFTWARE => "Software",
|
||||
Self::UNKNOWN => "Unknown",
|
||||
}
|
||||
@@ -167,8 +165,6 @@ fn get_encoder_api(encoder: &str, encoder_type: &EncoderType) -> EncoderAPI {
|
||||
EncoderAPI::VAAPI
|
||||
} else if encoder.starts_with("nv") {
|
||||
EncoderAPI::NVENC
|
||||
} else if encoder.starts_with("amf") {
|
||||
EncoderAPI::AMF
|
||||
} else {
|
||||
EncoderAPI::UNKNOWN
|
||||
}
|
||||
@@ -275,9 +271,9 @@ pub fn encoder_low_latency_params(
|
||||
encoder: &VideoEncoderInfo,
|
||||
_rate_control: &RateControl,
|
||||
framerate: u32,
|
||||
keyframe_dist_secs: u32,
|
||||
) -> VideoEncoderInfo {
|
||||
// 1 second keyframe interval for fast recovery, is this too taxing?
|
||||
let mut encoder_optz = encoder_gop_params(encoder, framerate);
|
||||
let mut encoder_optz = encoder_gop_params(encoder, framerate * keyframe_dist_secs);
|
||||
|
||||
match encoder_optz.encoder_api {
|
||||
EncoderAPI::QSV => {
|
||||
@@ -293,16 +289,6 @@ pub fn encoder_low_latency_params(
|
||||
encoder_optz.set_parameter("tune", "ultra-low-latency");
|
||||
encoder_optz.set_parameter("zerolatency", "true");
|
||||
}
|
||||
EncoderAPI::AMF => {
|
||||
encoder_optz.set_parameter("preset", "speed");
|
||||
let usage = match encoder_optz.codec {
|
||||
VideoCodec::H264 | VideoCodec::H265 => "ultra-low-latency",
|
||||
VideoCodec::AV1 => "low-latency",
|
||||
};
|
||||
if !usage.is_empty() {
|
||||
encoder_optz.set_parameter("usage", usage);
|
||||
}
|
||||
}
|
||||
EncoderAPI::SOFTWARE => match encoder_optz.name.as_str() {
|
||||
"openh264enc" => {
|
||||
encoder_optz.set_parameter("complexity", "low");
|
||||
@@ -330,6 +316,56 @@ pub fn encoder_low_latency_params(
|
||||
encoder_optz
|
||||
}
|
||||
|
||||
pub fn encoder_high_quality_params(
|
||||
encoder: &VideoEncoderInfo,
|
||||
_rate_control: &RateControl,
|
||||
framerate: u32,
|
||||
keyframe_dist_secs: u32,
|
||||
) -> VideoEncoderInfo {
|
||||
let mut encoder_optz = encoder_gop_params(encoder, framerate * keyframe_dist_secs);
|
||||
|
||||
match encoder_optz.encoder_api {
|
||||
EncoderAPI::QSV => {
|
||||
encoder_optz.set_parameter("low-latency", "false");
|
||||
encoder_optz.set_parameter("target-usage", "1");
|
||||
}
|
||||
EncoderAPI::VAAPI => {
|
||||
encoder_optz.set_parameter("target-usage", "1");
|
||||
}
|
||||
EncoderAPI::NVENC => {
|
||||
encoder_optz.set_parameter("multi-pass", "two-pass");
|
||||
encoder_optz.set_parameter("preset", "p7");
|
||||
encoder_optz.set_parameter("tune", "high-quality");
|
||||
encoder_optz.set_parameter("zerolatency", "false");
|
||||
encoder_optz.set_parameter("spatial-aq", "true");
|
||||
encoder_optz.set_parameter("rc-lookahead", "3");
|
||||
}
|
||||
EncoderAPI::SOFTWARE => match encoder_optz.name.as_str() {
|
||||
"openh264enc" => {
|
||||
encoder_optz.set_parameter("complexity", "high");
|
||||
encoder_optz.set_parameter("usage-type", "screen");
|
||||
}
|
||||
"x264enc" => {
|
||||
encoder_optz.set_parameter("rc-lookahead", "3");
|
||||
encoder_optz.set_parameter("speed-preset", "medium");
|
||||
}
|
||||
"svtav1enc" => {
|
||||
encoder_optz.set_parameter("preset", "8");
|
||||
encoder_optz.set_parameter("parameters-string", "lookahead=3");
|
||||
}
|
||||
"av1enc" => {
|
||||
encoder_optz.set_parameter("usage-profile", "realtime");
|
||||
encoder_optz.set_parameter("cpu-used", "8");
|
||||
encoder_optz.set_parameter("lag-in-frames", "3");
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
|
||||
encoder_optz
|
||||
}
|
||||
|
||||
pub fn get_compatible_encoders(gpus: &Vec<GPUInfo>) -> Vec<VideoEncoderInfo> {
|
||||
let mut encoders = Vec::new();
|
||||
let registry = gstreamer::Registry::get();
|
||||
@@ -427,16 +463,6 @@ pub fn get_compatible_encoders(gpus: &Vec<GPUInfo>) -> Vec<VideoEncoderInfo> {
|
||||
None
|
||||
}
|
||||
}
|
||||
EncoderAPI::AMF if element.has_property("device") => {
|
||||
let device_id = match element.property_value("device").get::<u32>() {
|
||||
Ok(v) => Some(v as usize),
|
||||
Err(_) => None,
|
||||
};
|
||||
|
||||
device_id.and_then(|id| {
|
||||
get_gpus_by_vendor(&gpus, GPUVendor::AMD).get(id).cloned()
|
||||
})
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
})
|
||||
@@ -549,7 +575,6 @@ pub fn get_best_compatible_encoder(
|
||||
score += match encoder.encoder_api {
|
||||
EncoderAPI::NVENC => 3,
|
||||
EncoderAPI::QSV => 3,
|
||||
EncoderAPI::AMF => 3,
|
||||
EncoderAPI::VAAPI => 2,
|
||||
EncoderAPI::SOFTWARE => 1,
|
||||
EncoderAPI::UNKNOWN => 0,
|
||||
|
||||
@@ -8,6 +8,7 @@ mod p2p;
|
||||
mod proto;
|
||||
|
||||
use crate::args::encoding_args;
|
||||
use crate::args::encoding_args::LatencyControl;
|
||||
use crate::enc_helper::{EncoderAPI, EncoderType};
|
||||
use crate::gpu::{GPUInfo, GPUVendor};
|
||||
use crate::input::controller::ControllerManager;
|
||||
@@ -130,11 +131,20 @@ fn handle_encoder_video_settings(
|
||||
args: &args::Args,
|
||||
video_encoder: &enc_helper::VideoEncoderInfo,
|
||||
) -> enc_helper::VideoEncoderInfo {
|
||||
let mut optimized_encoder = enc_helper::encoder_low_latency_params(
|
||||
let mut optimized_encoder = match args.encoding.video.latency_control {
|
||||
LatencyControl::LowestLatency => enc_helper::encoder_low_latency_params(
|
||||
&video_encoder,
|
||||
&args.encoding.video.rate_control,
|
||||
args.app.framerate,
|
||||
);
|
||||
args.encoding.video.keyframe_dist_secs,
|
||||
),
|
||||
LatencyControl::HighestQuality => enc_helper::encoder_high_quality_params(
|
||||
&video_encoder,
|
||||
&args.encoding.video.rate_control,
|
||||
args.app.framerate,
|
||||
args.encoding.video.keyframe_dist_secs,
|
||||
),
|
||||
};
|
||||
// Handle rate-control method
|
||||
match &args.encoding.video.rate_control {
|
||||
encoding_args::RateControl::CQP(cqp) => {
|
||||
@@ -429,39 +439,34 @@ async fn main() -> Result<(), Box<dyn Error>> {
|
||||
webrtcsink.set_property("do-retransmission", false);
|
||||
|
||||
/* Queues */
|
||||
let video_queue = gstreamer::ElementFactory::make("queue")
|
||||
// Sink queues
|
||||
let video_sink_queue = gstreamer::ElementFactory::make("queue").build()?;
|
||||
let audio_sink_queue = gstreamer::ElementFactory::make("queue").build()?;
|
||||
|
||||
// Source queues
|
||||
let video_source_queue = gstreamer::ElementFactory::make("queue")
|
||||
.property("max-size-buffers", 2u32)
|
||||
.property("max-size-time", 0u64)
|
||||
.property("max-size-bytes", 0u32)
|
||||
.build()?;
|
||||
|
||||
let audio_queue = gstreamer::ElementFactory::make("queue")
|
||||
let audio_source_queue = gstreamer::ElementFactory::make("queue")
|
||||
.property("max-size-buffers", 2u32)
|
||||
.property("max-size-time", 0u64)
|
||||
.property("max-size-bytes", 0u32)
|
||||
.build()?;
|
||||
|
||||
/* Clock Sync */
|
||||
let video_clocksync = gstreamer::ElementFactory::make("clocksync")
|
||||
.property("sync-to-first", true)
|
||||
.build()?;
|
||||
|
||||
let audio_clocksync = gstreamer::ElementFactory::make("clocksync")
|
||||
.property("sync-to-first", true)
|
||||
.build()?;
|
||||
|
||||
// Add elements to the pipeline
|
||||
pipeline.add_many(&[
|
||||
webrtcsink.upcast_ref(),
|
||||
&video_sink_queue,
|
||||
&audio_sink_queue,
|
||||
&video_encoder,
|
||||
&caps_filter,
|
||||
&video_queue,
|
||||
&video_clocksync,
|
||||
&video_source_queue,
|
||||
&video_source,
|
||||
&audio_encoder,
|
||||
&audio_capsfilter,
|
||||
&audio_queue,
|
||||
&audio_clocksync,
|
||||
&audio_source_queue,
|
||||
&audio_rate,
|
||||
&audio_converter,
|
||||
&audio_source,
|
||||
@@ -493,16 +498,24 @@ async fn main() -> Result<(), Box<dyn Error>> {
|
||||
&audio_converter,
|
||||
&audio_rate,
|
||||
&audio_capsfilter,
|
||||
&audio_queue,
|
||||
&audio_clocksync,
|
||||
&audio_source_queue,
|
||||
&audio_encoder,
|
||||
])?;
|
||||
|
||||
// Link audio parser to audio encoder if present, otherwise just webrtcsink
|
||||
if let Some(parser) = &audio_parser {
|
||||
gstreamer::Element::link_many(&[&audio_encoder, parser, webrtcsink.upcast_ref()])?;
|
||||
gstreamer::Element::link_many(&[
|
||||
&audio_encoder,
|
||||
parser,
|
||||
&audio_sink_queue,
|
||||
webrtcsink.upcast_ref(),
|
||||
])?;
|
||||
} else {
|
||||
gstreamer::Element::link_many(&[&audio_encoder, webrtcsink.upcast_ref()])?;
|
||||
gstreamer::Element::link_many(&[
|
||||
&audio_encoder,
|
||||
&audio_sink_queue,
|
||||
webrtcsink.upcast_ref(),
|
||||
])?;
|
||||
}
|
||||
|
||||
// With zero-copy..
|
||||
@@ -512,26 +525,20 @@ async fn main() -> Result<(), Box<dyn Error>> {
|
||||
gstreamer::Element::link_many(&[
|
||||
&video_source,
|
||||
&caps_filter,
|
||||
&video_queue,
|
||||
&video_clocksync,
|
||||
&video_source_queue,
|
||||
&vapostproc,
|
||||
&va_caps_filter,
|
||||
&video_encoder,
|
||||
])?;
|
||||
} else if video_encoder_info.encoder_api == EncoderAPI::NVENC {
|
||||
// NVENC pipeline
|
||||
gstreamer::Element::link_many(&[
|
||||
&video_source,
|
||||
&caps_filter,
|
||||
&video_encoder,
|
||||
])?;
|
||||
gstreamer::Element::link_many(&[&video_source, &caps_filter, &video_encoder])?;
|
||||
}
|
||||
} else {
|
||||
gstreamer::Element::link_many(&[
|
||||
&video_source,
|
||||
&caps_filter,
|
||||
&video_queue,
|
||||
&video_clocksync,
|
||||
&video_source_queue,
|
||||
&video_converter.unwrap(),
|
||||
&video_encoder,
|
||||
])?;
|
||||
@@ -539,21 +546,24 @@ async fn main() -> Result<(), Box<dyn Error>> {
|
||||
|
||||
// Link video parser if present with webrtcsink, otherwise just link webrtc sink
|
||||
if let Some(parser) = &video_parser {
|
||||
gstreamer::Element::link_many(&[&video_encoder, parser, webrtcsink.upcast_ref()])?;
|
||||
gstreamer::Element::link_many(&[
|
||||
&video_encoder,
|
||||
parser,
|
||||
&video_sink_queue,
|
||||
webrtcsink.upcast_ref(),
|
||||
])?;
|
||||
} else {
|
||||
gstreamer::Element::link_many(&[&video_encoder, webrtcsink.upcast_ref()])?;
|
||||
gstreamer::Element::link_many(&[
|
||||
&video_encoder,
|
||||
&video_sink_queue,
|
||||
webrtcsink.upcast_ref(),
|
||||
])?;
|
||||
}
|
||||
|
||||
// Make sure QOS is disabled to avoid latency
|
||||
video_encoder.set_property("qos", true);
|
||||
video_source.set_property("do-timestamp", &false);
|
||||
audio_source.set_property("do-timestamp", &false);
|
||||
|
||||
// Optimize latency of pipeline
|
||||
video_source
|
||||
.sync_state_with_parent()
|
||||
.expect("failed to sync with parent");
|
||||
video_source.set_property("do-timestamp", &true);
|
||||
audio_source.set_property("do-timestamp", &true);
|
||||
|
||||
pipeline.set_property("latency", &0u64);
|
||||
pipeline.set_property("async-handling", true);
|
||||
pipeline.set_property("message-forward", true);
|
||||
|
||||
@@ -55,9 +55,8 @@ impl NestriP2P {
|
||||
noise::Config::new,
|
||||
yamux::Config::default,
|
||||
)?
|
||||
.with_quic()
|
||||
.with_dns()?
|
||||
.with_websocket(noise::Config::new, yamux::Config::default)
|
||||
.await?
|
||||
.with_behaviour(|key| NestriBehaviour::new(key.public()))?
|
||||
.build(),
|
||||
));
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
// @generated
|
||||
// This file is @generated by prost-build.
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
|
||||
pub struct ProtoTimestampEntry {
|
||||
#[prost(string, tag="1")]
|
||||
pub stage: ::prost::alloc::string::String,
|
||||
#[prost(message, optional, tag="2")]
|
||||
pub time: ::core::option::Option<::prost_types::Timestamp>,
|
||||
}
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct ProtoLatencyTracker {
|
||||
#[prost(string, tag="1")]
|
||||
@@ -19,8 +17,7 @@ pub struct ProtoLatencyTracker {
|
||||
// Mouse messages
|
||||
|
||||
/// MouseMove message
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, Copy, PartialEq, ::prost::Message)]
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)]
|
||||
pub struct ProtoMouseMove {
|
||||
#[prost(int32, tag="1")]
|
||||
pub x: i32,
|
||||
@@ -28,8 +25,7 @@ pub struct ProtoMouseMove {
|
||||
pub y: i32,
|
||||
}
|
||||
/// MouseMoveAbs message
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, Copy, PartialEq, ::prost::Message)]
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)]
|
||||
pub struct ProtoMouseMoveAbs {
|
||||
#[prost(int32, tag="1")]
|
||||
pub x: i32,
|
||||
@@ -37,8 +33,7 @@ pub struct ProtoMouseMoveAbs {
|
||||
pub y: i32,
|
||||
}
|
||||
/// MouseWheel message
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, Copy, PartialEq, ::prost::Message)]
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)]
|
||||
pub struct ProtoMouseWheel {
|
||||
#[prost(int32, tag="1")]
|
||||
pub x: i32,
|
||||
@@ -46,15 +41,13 @@ pub struct ProtoMouseWheel {
|
||||
pub y: i32,
|
||||
}
|
||||
/// MouseKeyDown message
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, Copy, PartialEq, ::prost::Message)]
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)]
|
||||
pub struct ProtoMouseKeyDown {
|
||||
#[prost(int32, tag="1")]
|
||||
pub key: i32,
|
||||
}
|
||||
/// MouseKeyUp message
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, Copy, PartialEq, ::prost::Message)]
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)]
|
||||
pub struct ProtoMouseKeyUp {
|
||||
#[prost(int32, tag="1")]
|
||||
pub key: i32,
|
||||
@@ -62,24 +55,26 @@ pub struct ProtoMouseKeyUp {
|
||||
// Keyboard messages
|
||||
|
||||
/// KeyDown message
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, Copy, PartialEq, ::prost::Message)]
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)]
|
||||
pub struct ProtoKeyDown {
|
||||
#[prost(int32, tag="1")]
|
||||
pub key: i32,
|
||||
}
|
||||
/// KeyUp message
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, Copy, PartialEq, ::prost::Message)]
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)]
|
||||
pub struct ProtoKeyUp {
|
||||
#[prost(int32, tag="1")]
|
||||
pub key: i32,
|
||||
}
|
||||
// Clipboard message
|
||||
// message ProtoClipboard {
|
||||
// string content = 1; // Clipboard content
|
||||
// }
|
||||
|
||||
// Controller messages
|
||||
|
||||
/// ControllerAttach message
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
|
||||
pub struct ProtoControllerAttach {
|
||||
/// One of the following enums: "ps", "xbox" or "switch"
|
||||
#[prost(string, tag="1")]
|
||||
@@ -92,8 +87,7 @@ pub struct ProtoControllerAttach {
|
||||
pub session_id: ::prost::alloc::string::String,
|
||||
}
|
||||
/// ControllerDetach message
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
|
||||
pub struct ProtoControllerDetach {
|
||||
/// Session specific slot number (0-3)
|
||||
#[prost(int32, tag="1")]
|
||||
@@ -103,8 +97,7 @@ pub struct ProtoControllerDetach {
|
||||
pub session_id: ::prost::alloc::string::String,
|
||||
}
|
||||
/// ControllerRumble message
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
|
||||
pub struct ProtoControllerRumble {
|
||||
/// Session specific slot number (0-3)
|
||||
#[prost(int32, tag="1")]
|
||||
@@ -123,7 +116,6 @@ pub struct ProtoControllerRumble {
|
||||
pub duration: i32,
|
||||
}
|
||||
/// ControllerStateBatch - single message containing full or partial controller state
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct ProtoControllerStateBatch {
|
||||
/// Session specific slot number (0-3)
|
||||
@@ -188,8 +180,8 @@ pub mod proto_controller_state_batch {
|
||||
/// (if the ProtoBuf definition does not change) and safe for programmatic use.
|
||||
pub fn as_str_name(&self) -> &'static str {
|
||||
match self {
|
||||
UpdateType::FullState => "FULL_STATE",
|
||||
UpdateType::Delta => "DELTA",
|
||||
Self::FullState => "FULL_STATE",
|
||||
Self::Delta => "DELTA",
|
||||
}
|
||||
}
|
||||
/// Creates an enum from field names used in the ProtoBuf definition.
|
||||
@@ -204,8 +196,7 @@ pub mod proto_controller_state_batch {
|
||||
}
|
||||
// WebRTC + signaling
|
||||
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
|
||||
pub struct RtcIceCandidateInit {
|
||||
#[prost(string, tag="1")]
|
||||
pub candidate: ::prost::alloc::string::String,
|
||||
@@ -216,8 +207,7 @@ pub struct RtcIceCandidateInit {
|
||||
#[prost(string, optional, tag="4")]
|
||||
pub username_fragment: ::core::option::Option<::prost::alloc::string::String>,
|
||||
}
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
|
||||
pub struct RtcSessionDescriptionInit {
|
||||
#[prost(string, tag="1")]
|
||||
pub sdp: ::prost::alloc::string::String,
|
||||
@@ -225,29 +215,25 @@ pub struct RtcSessionDescriptionInit {
|
||||
pub r#type: ::prost::alloc::string::String,
|
||||
}
|
||||
/// ProtoICE message
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
|
||||
pub struct ProtoIce {
|
||||
#[prost(message, optional, tag="1")]
|
||||
pub candidate: ::core::option::Option<RtcIceCandidateInit>,
|
||||
}
|
||||
/// ProtoSDP message
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
|
||||
pub struct ProtoSdp {
|
||||
#[prost(message, optional, tag="1")]
|
||||
pub sdp: ::core::option::Option<RtcSessionDescriptionInit>,
|
||||
}
|
||||
/// ProtoRaw message
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
|
||||
pub struct ProtoRaw {
|
||||
#[prost(string, tag="1")]
|
||||
pub data: ::prost::alloc::string::String,
|
||||
}
|
||||
/// ProtoClientRequestRoomStream message
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
|
||||
pub struct ProtoClientRequestRoomStream {
|
||||
#[prost(string, tag="1")]
|
||||
pub room_name: ::prost::alloc::string::String,
|
||||
@@ -255,8 +241,7 @@ pub struct ProtoClientRequestRoomStream {
|
||||
pub session_id: ::prost::alloc::string::String,
|
||||
}
|
||||
/// ProtoClientDisconnected message
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
|
||||
pub struct ProtoClientDisconnected {
|
||||
#[prost(string, tag="1")]
|
||||
pub session_id: ::prost::alloc::string::String,
|
||||
@@ -264,13 +249,11 @@ pub struct ProtoClientDisconnected {
|
||||
pub controller_slots: ::prost::alloc::vec::Vec<i32>,
|
||||
}
|
||||
/// ProtoServerPushStream message
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
|
||||
pub struct ProtoServerPushStream {
|
||||
#[prost(string, tag="1")]
|
||||
pub room_name: ::prost::alloc::string::String,
|
||||
}
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct ProtoMessageBase {
|
||||
#[prost(string, tag="1")]
|
||||
@@ -278,7 +261,6 @@ pub struct ProtoMessageBase {
|
||||
#[prost(message, optional, tag="2")]
|
||||
pub latency: ::core::option::Option<ProtoLatencyTracker>,
|
||||
}
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct ProtoMessage {
|
||||
#[prost(message, optional, tag="1")]
|
||||
@@ -288,8 +270,7 @@ pub struct ProtoMessage {
|
||||
}
|
||||
/// Nested message and enum types in `ProtoMessage`.
|
||||
pub mod proto_message {
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Oneof)]
|
||||
#[derive(Clone, PartialEq, ::prost::Oneof)]
|
||||
pub enum Payload {
|
||||
/// Input types
|
||||
#[prost(message, tag="2")]
|
||||
@@ -304,6 +285,7 @@ pub mod proto_message {
|
||||
MouseKeyUp(super::ProtoMouseKeyUp),
|
||||
#[prost(message, tag="7")]
|
||||
KeyDown(super::ProtoKeyDown),
|
||||
/// ProtoClipboard clipboard = 9;
|
||||
#[prost(message, tag="8")]
|
||||
KeyUp(super::ProtoKeyUp),
|
||||
/// Controller input types
|
||||
|
||||