mirror of
https://github.com/bolucat/Archive.git
synced 2025-10-16 21:21:03 +08:00
Update On Mon Sep 2 20:34:11 CEST 2024
This commit is contained in:
1
.github/update.log
vendored
1
.github/update.log
vendored
@@ -751,3 +751,4 @@ Update On Wed Aug 28 20:33:23 CEST 2024
|
||||
Update On Fri Aug 30 20:32:59 CEST 2024
|
||||
Update On Sat Aug 31 20:34:23 CEST 2024
|
||||
Update On Sun Sep 1 20:30:49 CEST 2024
|
||||
Update On Mon Sep 2 20:34:00 CEST 2024
|
||||
|
@@ -40,8 +40,8 @@ subprojects {
|
||||
minSdk = 21
|
||||
targetSdk = 31
|
||||
|
||||
versionName = "2.10.3"
|
||||
versionCode = 210003
|
||||
versionName = "2.10.4"
|
||||
versionCode = 210004
|
||||
|
||||
resValue("string", "release_name", "v$versionName")
|
||||
resValue("integer", "release_code", "$versionCode")
|
||||
|
@@ -387,18 +387,18 @@ jobs:
|
||||
git fetch --tags
|
||||
echo "PREVERSION=$(git describe --tags --abbrev=0 HEAD)" >> $GITHUB_ENV
|
||||
|
||||
- name: Merge Alpha branch into Meta
|
||||
- name: Force push Alpha branch to Meta
|
||||
run: |
|
||||
git config --global user.email "github-actions[bot]@users.noreply.github.com"
|
||||
git config --global user.name "github-actions[bot]"
|
||||
git fetch origin Alpha:Alpha
|
||||
git merge Alpha
|
||||
git push origin Meta
|
||||
git push origin Alpha:Meta --force
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Tag the commit
|
||||
- name: Tag the commit on Alpha
|
||||
run: |
|
||||
git checkout Alpha
|
||||
git tag ${{ github.event.inputs.version }}
|
||||
git push origin ${{ github.event.inputs.version }}
|
||||
env:
|
||||
|
@@ -2,7 +2,6 @@ package updater
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
@@ -12,23 +11,26 @@ import (
|
||||
"sync"
|
||||
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
"github.com/metacubex/mihomo/log"
|
||||
)
|
||||
|
||||
var (
|
||||
ExternalUIURL string
|
||||
ExternalUIPath string
|
||||
ExternalUIFolder string
|
||||
ExternalUIName string
|
||||
)
|
||||
var (
|
||||
ErrIncompleteConf = errors.New("ExternalUI configure incomplete")
|
||||
AutoUpdateUI bool
|
||||
)
|
||||
|
||||
var xdMutex sync.Mutex
|
||||
|
||||
func UpdateUI() error {
|
||||
xdMutex.Lock()
|
||||
defer xdMutex.Unlock()
|
||||
|
||||
err := prepareUIPath()
|
||||
if err != nil {
|
||||
return fmt.Errorf("prepare UI path failed: %w", err)
|
||||
}
|
||||
|
||||
data, err := downloadForBytes(ExternalUIURL)
|
||||
if err != nil {
|
||||
return fmt.Errorf("can't download file: %w", err)
|
||||
@@ -40,7 +42,7 @@ func UpdateUI() error {
|
||||
}
|
||||
defer os.Remove(saved)
|
||||
|
||||
err = cleanup(ExternalUIFolder)
|
||||
err = cleanup(ExternalUIPath)
|
||||
if err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
return fmt.Errorf("cleanup exist file error: %w", err)
|
||||
@@ -52,28 +54,20 @@ func UpdateUI() error {
|
||||
return fmt.Errorf("can't extract zip file: %w", err)
|
||||
}
|
||||
|
||||
err = os.Rename(unzipFolder, ExternalUIFolder)
|
||||
err = os.Rename(unzipFolder, ExternalUIPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("can't rename folder: %w", err)
|
||||
return fmt.Errorf("rename UI folder failed: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func PrepareUIPath() error {
|
||||
if ExternalUIPath == "" || ExternalUIURL == "" {
|
||||
return ErrIncompleteConf
|
||||
}
|
||||
|
||||
if ExternalUIName != "" {
|
||||
ExternalUIFolder = filepath.Clean(path.Join(ExternalUIPath, ExternalUIName))
|
||||
func prepareUIPath() error {
|
||||
if _, err := os.Stat(ExternalUIPath); os.IsNotExist(err) {
|
||||
log.Infoln("dir %s does not exist, creating", ExternalUIPath)
|
||||
if err := os.MkdirAll(ExternalUIPath, os.ModePerm); err != nil {
|
||||
return err
|
||||
log.Warnln("create dir %s error: %s", ExternalUIPath, err)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ExternalUIFolder = ExternalUIPath
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@@ -7,7 +7,6 @@ import (
|
||||
"net"
|
||||
"net/netip"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"regexp"
|
||||
"strings"
|
||||
@@ -704,33 +703,23 @@ func parseGeneral(cfg *RawConfig) (*General, error) {
|
||||
}
|
||||
N.DisableKeepAlive = cfg.DisableKeepAlive
|
||||
|
||||
updater.ExternalUIPath = cfg.ExternalUI
|
||||
// checkout externalUI exist
|
||||
if updater.ExternalUIPath != "" {
|
||||
updater.ExternalUIPath = C.Path.Resolve(updater.ExternalUIPath)
|
||||
if _, err := os.Stat(updater.ExternalUIPath); os.IsNotExist(err) {
|
||||
defaultUIpath := path.Join(C.Path.HomeDir(), "ui")
|
||||
log.Warnln("external-ui: %s does not exist, creating folder in %s", updater.ExternalUIPath, defaultUIpath)
|
||||
if err := os.MkdirAll(defaultUIpath, os.ModePerm); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
updater.ExternalUIPath = defaultUIpath
|
||||
cfg.ExternalUI = defaultUIpath
|
||||
}
|
||||
}
|
||||
// checkout UIpath/name exist
|
||||
if cfg.ExternalUIName != "" {
|
||||
updater.ExternalUIName = cfg.ExternalUIName
|
||||
if cfg.ExternalUI != "" {
|
||||
updater.AutoUpdateUI = true
|
||||
updater.ExternalUIPath = C.Path.Resolve(cfg.ExternalUI)
|
||||
} else {
|
||||
updater.ExternalUIFolder = updater.ExternalUIPath
|
||||
}
|
||||
if cfg.ExternalUIURL != "" {
|
||||
updater.ExternalUIURL = cfg.ExternalUIURL
|
||||
// default externalUI path
|
||||
updater.ExternalUIPath = path.Join(C.Path.HomeDir(), "ui")
|
||||
}
|
||||
|
||||
err := updater.PrepareUIPath()
|
||||
if err != nil {
|
||||
log.Errorln("PrepareUIPath error: %s", err)
|
||||
// checkout UIpath/name exist
|
||||
if cfg.ExternalUIName != "" {
|
||||
updater.AutoUpdateUI = true
|
||||
updater.ExternalUIPath = path.Join(updater.ExternalUIPath, cfg.ExternalUIName)
|
||||
}
|
||||
|
||||
if cfg.ExternalUIURL != "" {
|
||||
updater.ExternalUIURL = cfg.ExternalUIURL
|
||||
}
|
||||
|
||||
return &General{
|
||||
|
@@ -381,12 +381,12 @@ func updateTunnels(tunnels []LC.Tunnel) {
|
||||
}
|
||||
|
||||
func initExternalUI() {
|
||||
if updater.ExternalUIFolder != "" {
|
||||
dirEntries, _ := os.ReadDir(updater.ExternalUIFolder)
|
||||
if updater.AutoUpdateUI {
|
||||
dirEntries, _ := os.ReadDir(updater.ExternalUIPath)
|
||||
if len(dirEntries) > 0 {
|
||||
log.Infoln("UI already exists")
|
||||
log.Infoln("UI already exists, skip downloading")
|
||||
} else {
|
||||
log.Infoln("UI not exists, downloading")
|
||||
log.Infoln("External UI downloading ...")
|
||||
updater.UpdateUI()
|
||||
}
|
||||
}
|
||||
|
@@ -60,9 +60,15 @@ func getGroupDelay(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if proxy.(*adapter.Proxy).Type() == C.URLTest {
|
||||
URLTestGroup := proxy.(*adapter.Proxy).ProxyAdapter.(*outboundgroup.URLTest)
|
||||
URLTestGroup.ForceSet("")
|
||||
switch proxy.(*adapter.Proxy).Type() {
|
||||
case C.URLTest:
|
||||
if urlTestGroup, ok := proxy.(*adapter.Proxy).ProxyAdapter.(*outboundgroup.URLTest); ok {
|
||||
urlTestGroup.ForceSet("")
|
||||
}
|
||||
case C.Fallback:
|
||||
if fallbackGroup, ok := proxy.(*adapter.Proxy).ProxyAdapter.(*outboundgroup.Fallback); ok {
|
||||
fallbackGroup.ForceSet("")
|
||||
}
|
||||
}
|
||||
|
||||
if proxy.(*adapter.Proxy).Type() != C.Selector {
|
||||
|
@@ -1,7 +1,6 @@
|
||||
package route
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
@@ -50,15 +49,9 @@ func upgradeCore(w http.ResponseWriter, r *http.Request) {
|
||||
func updateUI(w http.ResponseWriter, r *http.Request) {
|
||||
err := updater.UpdateUI()
|
||||
if err != nil {
|
||||
if errors.Is(err, updater.ErrIncompleteConf) {
|
||||
log.Warnln("%s", err)
|
||||
render.Status(r, http.StatusNotImplemented)
|
||||
render.JSON(w, r, newError(fmt.Sprintf("%s", err)))
|
||||
} else {
|
||||
log.Warnln("%s", err)
|
||||
render.Status(r, http.StatusInternalServerError)
|
||||
render.JSON(w, r, newError(fmt.Sprintf("%s", err)))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
@@ -107,7 +107,6 @@ require (
|
||||
golang.org/x/time v0.5.0 // indirect
|
||||
golang.org/x/tools v0.24.0 // indirect
|
||||
google.golang.org/protobuf v1.34.2 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
lukechampine.com/blake3 v1.3.0 // indirect
|
||||
)
|
||||
|
@@ -267,8 +267,6 @@ google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6h
|
||||
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
@@ -2,7 +2,6 @@ package updater
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
@@ -12,23 +11,26 @@ import (
|
||||
"sync"
|
||||
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
"github.com/metacubex/mihomo/log"
|
||||
)
|
||||
|
||||
var (
|
||||
ExternalUIURL string
|
||||
ExternalUIPath string
|
||||
ExternalUIFolder string
|
||||
ExternalUIName string
|
||||
)
|
||||
var (
|
||||
ErrIncompleteConf = errors.New("ExternalUI configure incomplete")
|
||||
AutoUpdateUI bool
|
||||
)
|
||||
|
||||
var xdMutex sync.Mutex
|
||||
|
||||
func UpdateUI() error {
|
||||
xdMutex.Lock()
|
||||
defer xdMutex.Unlock()
|
||||
|
||||
err := prepareUIPath()
|
||||
if err != nil {
|
||||
return fmt.Errorf("prepare UI path failed: %w", err)
|
||||
}
|
||||
|
||||
data, err := downloadForBytes(ExternalUIURL)
|
||||
if err != nil {
|
||||
return fmt.Errorf("can't download file: %w", err)
|
||||
@@ -40,7 +42,7 @@ func UpdateUI() error {
|
||||
}
|
||||
defer os.Remove(saved)
|
||||
|
||||
err = cleanup(ExternalUIFolder)
|
||||
err = cleanup(ExternalUIPath)
|
||||
if err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
return fmt.Errorf("cleanup exist file error: %w", err)
|
||||
@@ -52,28 +54,20 @@ func UpdateUI() error {
|
||||
return fmt.Errorf("can't extract zip file: %w", err)
|
||||
}
|
||||
|
||||
err = os.Rename(unzipFolder, ExternalUIFolder)
|
||||
err = os.Rename(unzipFolder, ExternalUIPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("can't rename folder: %w", err)
|
||||
return fmt.Errorf("rename UI folder failed: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func PrepareUIPath() error {
|
||||
if ExternalUIPath == "" || ExternalUIURL == "" {
|
||||
return ErrIncompleteConf
|
||||
}
|
||||
|
||||
if ExternalUIName != "" {
|
||||
ExternalUIFolder = filepath.Clean(path.Join(ExternalUIPath, ExternalUIName))
|
||||
func prepareUIPath() error {
|
||||
if _, err := os.Stat(ExternalUIPath); os.IsNotExist(err) {
|
||||
log.Infoln("dir %s does not exist, creating", ExternalUIPath)
|
||||
if err := os.MkdirAll(ExternalUIPath, os.ModePerm); err != nil {
|
||||
return err
|
||||
log.Warnln("create dir %s error: %s", ExternalUIPath, err)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ExternalUIFolder = ExternalUIPath
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@@ -7,7 +7,6 @@ import (
|
||||
"net"
|
||||
"net/netip"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"regexp"
|
||||
"strings"
|
||||
@@ -704,33 +703,23 @@ func parseGeneral(cfg *RawConfig) (*General, error) {
|
||||
}
|
||||
N.DisableKeepAlive = cfg.DisableKeepAlive
|
||||
|
||||
updater.ExternalUIPath = cfg.ExternalUI
|
||||
// checkout externalUI exist
|
||||
if updater.ExternalUIPath != "" {
|
||||
updater.ExternalUIPath = C.Path.Resolve(updater.ExternalUIPath)
|
||||
if _, err := os.Stat(updater.ExternalUIPath); os.IsNotExist(err) {
|
||||
defaultUIpath := path.Join(C.Path.HomeDir(), "ui")
|
||||
log.Warnln("external-ui: %s does not exist, creating folder in %s", updater.ExternalUIPath, defaultUIpath)
|
||||
if err := os.MkdirAll(defaultUIpath, os.ModePerm); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
updater.ExternalUIPath = defaultUIpath
|
||||
cfg.ExternalUI = defaultUIpath
|
||||
}
|
||||
}
|
||||
// checkout UIpath/name exist
|
||||
if cfg.ExternalUIName != "" {
|
||||
updater.ExternalUIName = cfg.ExternalUIName
|
||||
if cfg.ExternalUI != "" {
|
||||
updater.AutoUpdateUI = true
|
||||
updater.ExternalUIPath = C.Path.Resolve(cfg.ExternalUI)
|
||||
} else {
|
||||
updater.ExternalUIFolder = updater.ExternalUIPath
|
||||
}
|
||||
if cfg.ExternalUIURL != "" {
|
||||
updater.ExternalUIURL = cfg.ExternalUIURL
|
||||
// default externalUI path
|
||||
updater.ExternalUIPath = path.Join(C.Path.HomeDir(), "ui")
|
||||
}
|
||||
|
||||
err := updater.PrepareUIPath()
|
||||
if err != nil {
|
||||
log.Errorln("PrepareUIPath error: %s", err)
|
||||
// checkout UIpath/name exist
|
||||
if cfg.ExternalUIName != "" {
|
||||
updater.AutoUpdateUI = true
|
||||
updater.ExternalUIPath = path.Join(updater.ExternalUIPath, cfg.ExternalUIName)
|
||||
}
|
||||
|
||||
if cfg.ExternalUIURL != "" {
|
||||
updater.ExternalUIURL = cfg.ExternalUIURL
|
||||
}
|
||||
|
||||
return &General{
|
||||
|
@@ -381,12 +381,12 @@ func updateTunnels(tunnels []LC.Tunnel) {
|
||||
}
|
||||
|
||||
func initExternalUI() {
|
||||
if updater.ExternalUIFolder != "" {
|
||||
dirEntries, _ := os.ReadDir(updater.ExternalUIFolder)
|
||||
if updater.AutoUpdateUI {
|
||||
dirEntries, _ := os.ReadDir(updater.ExternalUIPath)
|
||||
if len(dirEntries) > 0 {
|
||||
log.Infoln("UI already exists")
|
||||
log.Infoln("UI already exists, skip downloading")
|
||||
} else {
|
||||
log.Infoln("UI not exists, downloading")
|
||||
log.Infoln("External UI downloading ...")
|
||||
updater.UpdateUI()
|
||||
}
|
||||
}
|
||||
|
@@ -60,9 +60,15 @@ func getGroupDelay(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if proxy.(*adapter.Proxy).Type() == C.URLTest {
|
||||
URLTestGroup := proxy.(*adapter.Proxy).ProxyAdapter.(*outboundgroup.URLTest)
|
||||
URLTestGroup.ForceSet("")
|
||||
switch proxy.(*adapter.Proxy).Type() {
|
||||
case C.URLTest:
|
||||
if urlTestGroup, ok := proxy.(*adapter.Proxy).ProxyAdapter.(*outboundgroup.URLTest); ok {
|
||||
urlTestGroup.ForceSet("")
|
||||
}
|
||||
case C.Fallback:
|
||||
if fallbackGroup, ok := proxy.(*adapter.Proxy).ProxyAdapter.(*outboundgroup.Fallback); ok {
|
||||
fallbackGroup.ForceSet("")
|
||||
}
|
||||
}
|
||||
|
||||
if proxy.(*adapter.Proxy).Type() != C.Selector {
|
||||
|
@@ -1,7 +1,6 @@
|
||||
package route
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
@@ -50,15 +49,9 @@ func upgradeCore(w http.ResponseWriter, r *http.Request) {
|
||||
func updateUI(w http.ResponseWriter, r *http.Request) {
|
||||
err := updater.UpdateUI()
|
||||
if err != nil {
|
||||
if errors.Is(err, updater.ErrIncompleteConf) {
|
||||
log.Warnln("%s", err)
|
||||
render.Status(r, http.StatusNotImplemented)
|
||||
render.JSON(w, r, newError(fmt.Sprintf("%s", err)))
|
||||
} else {
|
||||
log.Warnln("%s", err)
|
||||
render.Status(r, http.StatusInternalServerError)
|
||||
render.JSON(w, r, newError(fmt.Sprintf("%s", err)))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
@@ -1,2 +0,0 @@
|
||||
[target.aarch64-unknown-linux-gnu]
|
||||
linker = "aarch64-linux-gnu-gcc"
|
2
clash-nyanpasu/.github/workflows/ci.yml
vendored
2
clash-nyanpasu/.github/workflows/ci.yml
vendored
@@ -75,7 +75,7 @@ jobs:
|
||||
run: pnpm install --no-frozen-lockfile
|
||||
|
||||
- name: Prepare fronend
|
||||
run: pnpm -r build # Build frontend
|
||||
run: pnpm -r build && mkdir -p ./backend/tauri/.tmp/dist # Build frontend
|
||||
- name: Prepare sidecar and resources
|
||||
run: pnpm check
|
||||
- name: Lint
|
||||
|
@@ -51,31 +51,15 @@ jobs:
|
||||
run: |
|
||||
rustup install stable --profile minimal --no-self-update
|
||||
rustup default stable
|
||||
|
||||
- name: Setup aarch64 Toolchain
|
||||
- name: Setup Cargo binstall
|
||||
if: ${{ inputs.aarch64 == true }}
|
||||
uses: cargo-bins/cargo-binstall@main
|
||||
- name: Setup Cross Toolchain
|
||||
if: ${{ inputs.aarch64 == true }}
|
||||
run: |
|
||||
rustup target add aarch64-unknown-linux-gnu
|
||||
sudo apt install gcc-aarch64-linux-gnu -y
|
||||
sudo dpkg --add-architecture arm64
|
||||
cat <<EOF
|
||||
deb [arch=armhf,arm64] http://ports.ubuntu.com/ubuntu-ports jammy main restricted
|
||||
deb [arch=armhf,arm64] http://ports.ubuntu.com/ubuntu-ports jammy-updates main restricted
|
||||
deb [arch=armhf,arm64] http://ports.ubuntu.com/ubuntu-ports jammy universe
|
||||
deb [arch=armhf,arm64] http://ports.ubuntu.com/ubuntu-ports jammy-updates universe
|
||||
deb [arch=armhf,arm64] http://ports.ubuntu.com/ubuntu-ports jammy multiverse
|
||||
deb [arch=armhf,arm64] http://ports.ubuntu.com/ubuntu-ports jammy-updates multiverse
|
||||
deb [arch=armhf,arm64] http://ports.ubuntu.com/ubuntu-ports jammy-backports main restricted universe multiverse
|
||||
deb [arch=armhf,arm64] http://ports.ubuntu.com/ubuntu-ports jammy-security main restricted
|
||||
deb [arch=armhf,arm64] http://ports.ubuntu.com/ubuntu-ports jammy-security universe
|
||||
deb [arch=armhf,arm64] http://ports.ubuntu.com/ubuntu-ports jammy-security multiverse
|
||||
EOF | sudo tee /etc/apt/sources.list.d/arm64.list
|
||||
sudo apt update -y && sudo apt upgrade -y
|
||||
apt-get install -y libncurses6:arm64 libtinfo6:arm64 linux-libc-dev:arm64 libncursesw6:arm64 libssl3:arm64 libcups2:arm64
|
||||
apt-get install -y --no-install-recommends g++-aarch64-linux-gnu libc6-dev-arm64-cross libwebkit2gtk-4.0-dev:arm64 libgtk-3-dev:arm64 patchelf:arm64 librsvg2-dev:arm64 libayatana-appindicator3-dev:arm64
|
||||
|
||||
- name: Setup x86_64 Toolchain
|
||||
if: ${{ inputs.aarch64 == false }}
|
||||
cargo binstall -y cross
|
||||
- name: Setup Toolchain
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y libgtk-3-dev webkit2gtk-4.0 libappindicator3-dev librsvg2-dev patchelf openssl
|
||||
@@ -137,17 +121,18 @@ jobs:
|
||||
CXX_aarch64_unknown_linux_gnu: aarch64-linux-gnu-g++
|
||||
PKG_CONFIG_PATH: /usr/lib/aarch64-linux-gnu/pkgconfig
|
||||
PKG_CONFIG_ALLOW_CROSS: 1
|
||||
|
||||
run: |
|
||||
${{ inputs.nightly == true && 'pnpm build:nightly --target aarch64-unknown-linux-gnu -b "rpm,deb,updater"' || 'pnpm build --target aarch64-unknown-linux-gnu -b "rpm,deb,updater"' }}
|
||||
gh release upload ${{ inputs.tag }} ./backend/target/release/bundle/*.deb ./backend/target/release/bundle/*.rpm --clobber
|
||||
${{ inputs.nightly == true && 'pnpm build:nightly -r cross --target aarch64-unknown-linux-gnu -b "rpm,deb,updater"' || 'pnpm build -r cross --target aarch64-unknown-linux-gnu -b "rpm,deb,updater"' }}
|
||||
find ./backend/target \( -name "*.deb" -o -name "*.rpm" \) | while read file; do
|
||||
gh release upload ${{ inputs.tag }} "$file" --clobber
|
||||
done
|
||||
|
||||
- name: Calc the archive signature
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
TAG_NAME=${{ inputs.tag }}
|
||||
find ./backend/target/release/bundle \( -name "*.deb" -o -name "*.rpm" \) | while read file; do
|
||||
find ./backend/target \( -name "*.deb" -o -name "*.rpm" \) | while read file; do
|
||||
sha_file="$file.sha256"
|
||||
|
||||
if [[ ! -f "$sha_file" ]]; then
|
||||
|
23
clash-nyanpasu/.github/workflows/stale.yml
vendored
Normal file
23
clash-nyanpasu/.github/workflows/stale.yml
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
name: "Close stale issues and PRs"
|
||||
on:
|
||||
schedule:
|
||||
- cron: "30 1 * * *"
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: write # only for delete-branch option
|
||||
issues: write
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v9
|
||||
with:
|
||||
stale-issue-message: "This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days."
|
||||
close-issue-message: "This issue is closed because it has been stale for 5 days with no activity."
|
||||
days-before-stale: 30
|
||||
days-before-close: 5
|
||||
stale-issue-label: "S: Stale"
|
||||
only-issue-labels: "S: Untriaged"
|
@@ -27,8 +27,8 @@ jobs:
|
||||
tag: "pre-release"
|
||||
secrets: inherit
|
||||
|
||||
linux_build:
|
||||
name: Linux Build
|
||||
linux_amd64_build:
|
||||
name: Linux amd64 Build
|
||||
uses: ./.github/workflows/deps-build-linux.yaml
|
||||
needs: [delete_current_releases]
|
||||
with:
|
||||
@@ -36,6 +36,16 @@ jobs:
|
||||
tag: "pre-release"
|
||||
secrets: inherit
|
||||
|
||||
linux_aarch64_build:
|
||||
name: Linux aarch64 Build
|
||||
uses: ./.github/workflows/deps-build-linux.yaml
|
||||
needs: [delete_current_releases]
|
||||
with:
|
||||
nightly: true
|
||||
tag: "pre-release"
|
||||
aarch64: true
|
||||
secrets: inherit
|
||||
|
||||
macos_amd64_build:
|
||||
name: macOS amd64 Build
|
||||
uses: ./.github/workflows/deps-build-macos.yaml
|
||||
@@ -58,7 +68,14 @@ jobs:
|
||||
|
||||
update_tag:
|
||||
name: Update tag
|
||||
needs: [windows_build, linux_build, macos_amd64_build, macos_aarch64_build]
|
||||
needs:
|
||||
[
|
||||
windows_build,
|
||||
linux_amd64_build,
|
||||
linux_aarch64_build,
|
||||
macos_amd64_build,
|
||||
macos_aarch64_build,
|
||||
]
|
||||
uses: ./.github/workflows/deps-update-tag.yaml
|
||||
with:
|
||||
tag: "pre-release"
|
||||
|
2
clash-nyanpasu/.gitignore
vendored
2
clash-nyanpasu/.gitignore
vendored
@@ -16,3 +16,5 @@ tauri.preview.conf.json
|
||||
.idea
|
||||
|
||||
*.tsbuildinfo
|
||||
|
||||
**/.tmp
|
||||
|
1
clash-nyanpasu/backend/Cargo.lock
generated
1
clash-nyanpasu/backend/Cargo.lock
generated
@@ -1273,6 +1273,7 @@ dependencies = [
|
||||
"objc",
|
||||
"once_cell",
|
||||
"open 5.3.0",
|
||||
"openssl",
|
||||
"os_pipe",
|
||||
"oxc_allocator",
|
||||
"oxc_ast",
|
||||
|
16
clash-nyanpasu/backend/Cross.toml
Normal file
16
clash-nyanpasu/backend/Cross.toml
Normal file
@@ -0,0 +1,16 @@
|
||||
[target.aarch64-unknown-linux-gnu]
|
||||
image = "ghcr.io/cross-rs/aarch64-unknown-linux-gnu:edge"
|
||||
pre-build = [
|
||||
"dpkg --add-architecture $CROSS_DEB_ARCH",
|
||||
"""apt-get update && apt-get -y install \
|
||||
libwebkit2gtk-4.0-dev:$CROSS_DEB_ARCH \
|
||||
libgtk-3-dev:$CROSS_DEB_ARCH \
|
||||
libayatana-appindicator3-dev:$CROSS_DEB_ARCH \
|
||||
librsvg2-dev:$CROSS_DEB_ARCH \
|
||||
libpango1.0-dev:$CROSS_DEB_ARCH \
|
||||
libcairo2-dev:$CROSS_DEB_ARCH \
|
||||
libatk1.0-dev:$CROSS_DEB_ARCH \
|
||||
libsoup2.4-dev:$CROSS_DEB_ARCH \
|
||||
libssl-dev:$CROSS_DEB_ARCH
|
||||
""",
|
||||
]
|
@@ -137,6 +137,10 @@ mlua = { version = "0.9", features = [
|
||||
] }
|
||||
enumflags2 = "0.7"
|
||||
sha2 = "0.10"
|
||||
|
||||
[target.'cfg(all(target_os = "linux", target_arch = "aarch64"))'.dependencies]
|
||||
openssl = { version = "0.10", features = ["vendored"] }
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dependencies]
|
||||
cocoa = "0.25.0"
|
||||
objc = "0.2.7"
|
||||
@@ -185,3 +189,4 @@ verge-dev = []
|
||||
default-meta = []
|
||||
devtools = ["tauri/devtools"]
|
||||
deadlock-detection = ["parking_lot/deadlock_detection"]
|
||||
openssl_vendored = ["openssl/vendored"]
|
||||
|
@@ -1,19 +1,48 @@
|
||||
use chrono::{DateTime, SecondsFormat, Utc};
|
||||
use rustc_version::version_meta;
|
||||
use serde::Deserialize;
|
||||
use std::{env, fs::read, process::Command};
|
||||
use std::{
|
||||
env,
|
||||
fs::{exists, read},
|
||||
process::Command,
|
||||
};
|
||||
#[derive(Deserialize)]
|
||||
struct PackageJson {
|
||||
version: String, // we only need the version
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct TauriJson {
|
||||
package: PackageJson,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct GitInfo {
|
||||
hash: String,
|
||||
author: String,
|
||||
time: String,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let mut pkg_json = read("../../package.json").unwrap();
|
||||
let pkg_json: PackageJson = simd_json::from_slice(&mut pkg_json).unwrap();
|
||||
let version = semver::Version::parse(pkg_json.version.as_str()).unwrap();
|
||||
let version: String = if let Ok(true) = exists("../../package.json") {
|
||||
let mut raw = read("../../package.json").unwrap();
|
||||
let pkg_json: PackageJson = simd_json::from_slice(&mut raw).unwrap();
|
||||
pkg_json.version
|
||||
} else {
|
||||
let mut raw = read("./tauri.conf.json").unwrap(); // TODO: fix it when windows arm64 need it
|
||||
let tauri_json: TauriJson = simd_json::from_slice(&mut raw).unwrap();
|
||||
tauri_json.package.version
|
||||
};
|
||||
let version = semver::Version::parse(&version).unwrap();
|
||||
let is_prerelase = !version.pre.is_empty();
|
||||
println!("cargo:rustc-env=NYANPASU_VERSION={}", pkg_json.version);
|
||||
println!("cargo:rustc-env=NYANPASU_VERSION={}", version);
|
||||
// Git Information
|
||||
let (commit_hash, commit_author, commit_date) = if let Ok(true) = exists("./.tmp/git-info.json")
|
||||
{
|
||||
let mut git_info = read("./.tmp/git-info.json").unwrap();
|
||||
let git_info: GitInfo = simd_json::from_slice(&mut git_info).unwrap();
|
||||
(git_info.hash, git_info.author, git_info.time)
|
||||
} else {
|
||||
let output = Command::new("git")
|
||||
.args([
|
||||
"show",
|
||||
@@ -22,16 +51,23 @@ fn main() {
|
||||
"--no-notes",
|
||||
])
|
||||
.output()
|
||||
.unwrap();
|
||||
.expect("Failed to execute git command");
|
||||
// println!("{}", String::from_utf8(output.stderr.clone()).unwrap());
|
||||
let command_args: Vec<String> = String::from_utf8(output.stdout)
|
||||
.unwrap()
|
||||
.replace('\'', "")
|
||||
.split(',')
|
||||
.map(String::from)
|
||||
.collect();
|
||||
println!("cargo:rustc-env=COMMIT_HASH={}", command_args[0]);
|
||||
println!("cargo:rustc-env=COMMIT_AUTHOR={}", command_args[1]);
|
||||
let commit_date = DateTime::parse_from_rfc3339(command_args[2].as_str())
|
||||
(
|
||||
command_args[0].clone(),
|
||||
command_args[1].clone(),
|
||||
command_args[2].clone(),
|
||||
)
|
||||
};
|
||||
println!("cargo:rustc-env=COMMIT_HASH={}", commit_hash);
|
||||
println!("cargo:rustc-env=COMMIT_AUTHOR={}", commit_author);
|
||||
let commit_date = DateTime::parse_from_rfc3339(&commit_date)
|
||||
.unwrap()
|
||||
.with_timezone(&Utc)
|
||||
.to_rfc3339_opts(SecondsFormat::Millis, true);
|
||||
|
@@ -4,10 +4,10 @@
|
||||
"version": "1.6.0"
|
||||
},
|
||||
"build": {
|
||||
"distDir": "../../frontend/nyanpasu/dist",
|
||||
"distDir": "./.tmp/dist",
|
||||
"devPath": "http://localhost:3000/",
|
||||
"beforeDevCommand": "pnpm run web:dev",
|
||||
"beforeBuildCommand": "pnpm run web:build"
|
||||
"beforeBuildCommand": "pnpm run-p web:build generate:git-info && echo $(pwd) && rm -rf ./tauri/.tmp/dist && mv ../frontend/nyanpasu/dist ./tauri/.tmp/dist"
|
||||
},
|
||||
"tauri": {
|
||||
"systemTray": {
|
||||
|
@@ -56,7 +56,7 @@
|
||||
"@vitejs/plugin-react-swc": "3.7.0",
|
||||
"clsx": "2.1.1",
|
||||
"sass": "1.77.8",
|
||||
"shiki": "1.15.2",
|
||||
"shiki": "1.16.1",
|
||||
"tailwindcss-textshadow": "2.1.3",
|
||||
"unplugin-auto-import": "0.18.2",
|
||||
"unplugin-icons": "0.19.2",
|
||||
|
@@ -44,6 +44,6 @@
|
||||
"sass": "1.77.8",
|
||||
"tailwind-merge": "2.5.2",
|
||||
"typescript-plugin-css-modules": "5.1.0",
|
||||
"vite-plugin-dts": "4.0.3"
|
||||
"vite-plugin-dts": "4.1.0"
|
||||
}
|
||||
}
|
||||
|
@@ -43,6 +43,7 @@
|
||||
"publish": "tsx scripts/publish.ts",
|
||||
"portable": "tsx scripts/portable.ts",
|
||||
"upload:osx-aarch64": "tsx scripts/osx-aarch64-upload.ts",
|
||||
"generate:git-info": "tsx scripts/generate-git-info.ts",
|
||||
"generate:manifest": "run-p generate:manifest:*",
|
||||
"generate:manifest:latest-version": "tsx scripts/generate-latest-version.ts",
|
||||
"prepare": "husky",
|
||||
@@ -61,7 +62,7 @@
|
||||
"@tauri-apps/cli": "1.6.1",
|
||||
"@types/fs-extra": "11.0.4",
|
||||
"@types/lodash-es": "4.17.12",
|
||||
"@types/node": "22.5.1",
|
||||
"@types/node": "22.5.2",
|
||||
"@typescript-eslint/eslint-plugin": "8.3.0",
|
||||
"@typescript-eslint/parser": "8.3.0",
|
||||
"autoprefixer": "10.4.20",
|
||||
@@ -80,10 +81,10 @@
|
||||
"eslint-plugin-react": "7.35.0",
|
||||
"eslint-plugin-react-compiler": "0.0.0-experimental-f8a5409-20240829",
|
||||
"eslint-plugin-react-hooks": "4.6.2",
|
||||
"knip": "5.28.0",
|
||||
"knip": "5.29.1",
|
||||
"lint-staged": "15.2.9",
|
||||
"npm-run-all2": "6.2.2",
|
||||
"postcss": "8.4.42",
|
||||
"postcss": "8.4.44",
|
||||
"postcss-html": "1.7.0",
|
||||
"postcss-import": "16.1.0",
|
||||
"postcss-scss": "4.0.9",
|
||||
|
257
clash-nyanpasu/pnpm-lock.yaml
generated
257
clash-nyanpasu/pnpm-lock.yaml
generated
@@ -24,7 +24,7 @@ importers:
|
||||
devDependencies:
|
||||
'@commitlint/cli':
|
||||
specifier: 19.4.1
|
||||
version: 19.4.1(@types/node@22.5.1)(typescript@5.5.4)
|
||||
version: 19.4.1(@types/node@22.5.2)(typescript@5.5.4)
|
||||
'@commitlint/config-conventional':
|
||||
specifier: 19.4.1
|
||||
version: 19.4.1
|
||||
@@ -41,8 +41,8 @@ importers:
|
||||
specifier: 4.17.12
|
||||
version: 4.17.12
|
||||
'@types/node':
|
||||
specifier: 22.5.1
|
||||
version: 22.5.1
|
||||
specifier: 22.5.2
|
||||
version: 22.5.2
|
||||
'@typescript-eslint/eslint-plugin':
|
||||
specifier: 8.3.0
|
||||
version: 8.3.0(@typescript-eslint/parser@8.3.0(eslint@8.57.0)(typescript@5.5.4))(eslint@8.57.0)(typescript@5.5.4)
|
||||
@@ -51,7 +51,7 @@ importers:
|
||||
version: 8.3.0(eslint@8.57.0)(typescript@5.5.4)
|
||||
autoprefixer:
|
||||
specifier: 10.4.20
|
||||
version: 10.4.20(postcss@8.4.42)
|
||||
version: 10.4.20(postcss@8.4.44)
|
||||
conventional-changelog-conventionalcommits:
|
||||
specifier: 8.0.0
|
||||
version: 8.0.0
|
||||
@@ -98,8 +98,8 @@ importers:
|
||||
specifier: 4.6.2
|
||||
version: 4.6.2(eslint@8.57.0)
|
||||
knip:
|
||||
specifier: 5.28.0
|
||||
version: 5.28.0(@types/node@22.5.1)(typescript@5.5.4)
|
||||
specifier: 5.29.1
|
||||
version: 5.29.1(@types/node@22.5.2)(typescript@5.5.4)
|
||||
lint-staged:
|
||||
specifier: 15.2.9
|
||||
version: 15.2.9
|
||||
@@ -107,17 +107,17 @@ importers:
|
||||
specifier: 6.2.2
|
||||
version: 6.2.2
|
||||
postcss:
|
||||
specifier: 8.4.42
|
||||
version: 8.4.42
|
||||
specifier: 8.4.44
|
||||
version: 8.4.44
|
||||
postcss-html:
|
||||
specifier: 1.7.0
|
||||
version: 1.7.0
|
||||
postcss-import:
|
||||
specifier: 16.1.0
|
||||
version: 16.1.0(postcss@8.4.42)
|
||||
version: 16.1.0(postcss@8.4.44)
|
||||
postcss-scss:
|
||||
specifier: 4.0.9
|
||||
version: 4.0.9(postcss@8.4.42)
|
||||
version: 4.0.9(postcss@8.4.44)
|
||||
prettier:
|
||||
specifier: 3.3.3
|
||||
version: 3.3.3
|
||||
@@ -199,7 +199,7 @@ importers:
|
||||
version: 11.13.0(@emotion/react@11.13.3(react@19.0.0-rc-e948a5ac-20240807)(types-react@19.0.0-rc.1))(react@19.0.0-rc-e948a5ac-20240807)(types-react@19.0.0-rc.1)
|
||||
'@generouted/react-router':
|
||||
specifier: 1.19.6
|
||||
version: 1.19.6(react-router-dom@6.26.1(react-dom@19.0.0-rc-e948a5ac-20240807(react@19.0.0-rc-e948a5ac-20240807))(react@19.0.0-rc-e948a5ac-20240807))(react@19.0.0-rc-e948a5ac-20240807)(vite@5.4.2(@types/node@22.5.1)(less@4.2.0)(sass@1.77.8)(stylus@0.62.0))
|
||||
version: 1.19.6(react-router-dom@6.26.1(react-dom@19.0.0-rc-e948a5ac-20240807(react@19.0.0-rc-e948a5ac-20240807))(react@19.0.0-rc-e948a5ac-20240807))(react@19.0.0-rc-e948a5ac-20240807)(vite@5.4.2(@types/node@22.5.2)(less@4.2.0)(sass@1.77.8)(stylus@0.62.0))
|
||||
'@juggle/resize-observer':
|
||||
specifier: 3.4.0
|
||||
version: 3.4.0
|
||||
@@ -311,10 +311,10 @@ importers:
|
||||
version: types-react-dom@19.0.0-rc.1
|
||||
'@vitejs/plugin-react':
|
||||
specifier: 4.3.1
|
||||
version: 4.3.1(vite@5.4.2(@types/node@22.5.1)(less@4.2.0)(sass@1.77.8)(stylus@0.62.0))
|
||||
version: 4.3.1(vite@5.4.2(@types/node@22.5.2)(less@4.2.0)(sass@1.77.8)(stylus@0.62.0))
|
||||
'@vitejs/plugin-react-swc':
|
||||
specifier: 3.7.0
|
||||
version: 3.7.0(vite@5.4.2(@types/node@22.5.1)(less@4.2.0)(sass@1.77.8)(stylus@0.62.0))
|
||||
version: 3.7.0(vite@5.4.2(@types/node@22.5.2)(less@4.2.0)(sass@1.77.8)(stylus@0.62.0))
|
||||
clsx:
|
||||
specifier: 2.1.1
|
||||
version: 2.1.1
|
||||
@@ -322,8 +322,8 @@ importers:
|
||||
specifier: 1.77.8
|
||||
version: 1.77.8
|
||||
shiki:
|
||||
specifier: 1.15.2
|
||||
version: 1.15.2
|
||||
specifier: 1.16.1
|
||||
version: 1.16.1
|
||||
tailwindcss-textshadow:
|
||||
specifier: 2.1.3
|
||||
version: 2.1.3
|
||||
@@ -335,19 +335,19 @@ importers:
|
||||
version: 0.19.2(@svgr/core@8.1.0(typescript@5.5.4))
|
||||
vite:
|
||||
specifier: 5.4.2
|
||||
version: 5.4.2(@types/node@22.5.1)(less@4.2.0)(sass@1.77.8)(stylus@0.62.0)
|
||||
version: 5.4.2(@types/node@22.5.2)(less@4.2.0)(sass@1.77.8)(stylus@0.62.0)
|
||||
vite-plugin-monaco-editor:
|
||||
specifier: npm:vite-plugin-monaco-editor-new@1.1.3
|
||||
version: vite-plugin-monaco-editor-new@1.1.3(monaco-editor@0.51.0)
|
||||
vite-plugin-sass-dts:
|
||||
specifier: 1.3.25
|
||||
version: 1.3.25(postcss@8.4.42)(prettier@3.3.3)(sass@1.77.8)(vite@5.4.2(@types/node@22.5.1)(less@4.2.0)(sass@1.77.8)(stylus@0.62.0))
|
||||
version: 1.3.25(postcss@8.4.44)(prettier@3.3.3)(sass@1.77.8)(vite@5.4.2(@types/node@22.5.2)(less@4.2.0)(sass@1.77.8)(stylus@0.62.0))
|
||||
vite-plugin-svgr:
|
||||
specifier: 4.2.0
|
||||
version: 4.2.0(rollup@4.21.0)(typescript@5.5.4)(vite@5.4.2(@types/node@22.5.1)(less@4.2.0)(sass@1.77.8)(stylus@0.62.0))
|
||||
version: 4.2.0(rollup@4.21.0)(typescript@5.5.4)(vite@5.4.2(@types/node@22.5.2)(less@4.2.0)(sass@1.77.8)(stylus@0.62.0))
|
||||
vite-tsconfig-paths:
|
||||
specifier: 5.0.1
|
||||
version: 5.0.1(typescript@5.5.4)(vite@5.4.2(@types/node@22.5.1)(less@4.2.0)(sass@1.77.8)(stylus@0.62.0))
|
||||
version: 5.0.1(typescript@5.5.4)(vite@5.4.2(@types/node@22.5.2)(less@4.2.0)(sass@1.77.8)(stylus@0.62.0))
|
||||
|
||||
frontend/ui:
|
||||
dependencies:
|
||||
@@ -380,7 +380,7 @@ importers:
|
||||
version: types-react@19.0.0-rc.1
|
||||
'@vitejs/plugin-react':
|
||||
specifier: 4.3.1
|
||||
version: 4.3.1(vite@5.4.2(@types/node@22.5.1)(less@4.2.0)(sass@1.77.8)(stylus@0.62.0))
|
||||
version: 4.3.1(vite@5.4.2(@types/node@22.5.2)(less@4.2.0)(sass@1.77.8)(stylus@0.62.0))
|
||||
ahooks:
|
||||
specifier: 3.8.1
|
||||
version: 3.8.1(react@19.0.0-rc-e948a5ac-20240807)
|
||||
@@ -404,10 +404,10 @@ importers:
|
||||
version: 17.5.1(react-dom@19.0.0-rc-e948a5ac-20240807(react@19.0.0-rc-e948a5ac-20240807))(react@19.0.0-rc-e948a5ac-20240807)
|
||||
vite:
|
||||
specifier: 5.4.2
|
||||
version: 5.4.2(@types/node@22.5.1)(less@4.2.0)(sass@1.77.8)(stylus@0.62.0)
|
||||
version: 5.4.2(@types/node@22.5.2)(less@4.2.0)(sass@1.77.8)(stylus@0.62.0)
|
||||
vite-tsconfig-paths:
|
||||
specifier: 5.0.1
|
||||
version: 5.0.1(typescript@5.5.4)(vite@5.4.2(@types/node@22.5.1)(less@4.2.0)(sass@1.77.8)(stylus@0.62.0))
|
||||
version: 5.0.1(typescript@5.5.4)(vite@5.4.2(@types/node@22.5.2)(less@4.2.0)(sass@1.77.8)(stylus@0.62.0))
|
||||
devDependencies:
|
||||
'@emotion/react':
|
||||
specifier: 11.13.3
|
||||
@@ -431,8 +431,8 @@ importers:
|
||||
specifier: 5.1.0
|
||||
version: 5.1.0(typescript@5.5.4)
|
||||
vite-plugin-dts:
|
||||
specifier: 4.0.3
|
||||
version: 4.0.3(@types/node@22.5.1)(rollup@4.21.0)(typescript@5.5.4)(vite@5.4.2(@types/node@22.5.1)(less@4.2.0)(sass@1.77.8)(stylus@0.62.0))
|
||||
specifier: 4.1.0
|
||||
version: 4.1.0(@types/node@22.5.2)(rollup@4.21.0)(typescript@5.5.4)(vite@5.4.2(@types/node@22.5.2)(less@4.2.0)(sass@1.77.8)(stylus@0.62.0))
|
||||
|
||||
scripts:
|
||||
dependencies:
|
||||
@@ -2048,8 +2048,11 @@ packages:
|
||||
'@rushstack/ts-command-line@4.22.3':
|
||||
resolution: {integrity: sha512-edMpWB3QhFFZ4KtSzS8WNjBgR4PXPPOVrOHMbb7kNpmQ1UFS9HdVtjCXg1H5fG+xYAbeE+TMPcVPUyX2p84STA==}
|
||||
|
||||
'@shikijs/core@1.15.2':
|
||||
resolution: {integrity: sha512-hi6XZuwHYn6bU4wtXZxST8ynM55aiU2+rVU9aPIrSxqKmEKl4d65puwGsggwcZWTET+7zGXKe7AUj46iQ8Aq8w==}
|
||||
'@shikijs/core@1.16.1':
|
||||
resolution: {integrity: sha512-aI0hBtw+a6KsJp2jcD4YuQqKpeCbURMZbhHVozDknJpm+KJqeMRkEnfBC8BaKE/5XC+uofPgCLsa/TkTk0Ba0w==}
|
||||
|
||||
'@shikijs/vscode-textmate@9.2.0':
|
||||
resolution: {integrity: sha512-5FinaOp6Vdh/dl4/yaOTh0ZeKch+rYS8DUb38V3GMKYVkdqzxw53lViRKUYkVILRiVQT7dcPC7VvAKOR73zVtQ==}
|
||||
|
||||
'@sindresorhus/is@4.6.0':
|
||||
resolution: {integrity: sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==}
|
||||
@@ -2492,8 +2495,8 @@ packages:
|
||||
'@types/node@22.5.0':
|
||||
resolution: {integrity: sha512-DkFrJOe+rfdHTqqMg0bSNlGlQ85hSoh2TPzZyhHsXnMtligRWpxUySiyw8FY14ITt24HVCiQPWxS3KO/QlGmWg==}
|
||||
|
||||
'@types/node@22.5.1':
|
||||
resolution: {integrity: sha512-KkHsxej0j9IW1KKOOAA/XBA0z08UFSrRQHErzEfA3Vgq57eXIMYboIlHJuYIfd+lwCQjtKqUu3UnmKbtUc9yRw==}
|
||||
'@types/node@22.5.2':
|
||||
resolution: {integrity: sha512-acJsPTEqYqulZS/Yp/S3GgeE6GZ0qYODUR8aVr/DkhHQ8l9nd4j5x1/ZJy9/gHrRlFMqkO6i0I3E27Alu4jjPg==}
|
||||
|
||||
'@types/parse-json@4.0.2':
|
||||
resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==}
|
||||
@@ -4623,8 +4626,8 @@ packages:
|
||||
resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
knip@5.28.0:
|
||||
resolution: {integrity: sha512-3nlqKCHFCfXp4VDP570ly7HLCyIM3JyLM+msr2l3HtQJ1NeraBrj6AQE80SFIyu8nOJZZpiZQWmiXq5RSczqsQ==}
|
||||
knip@5.29.1:
|
||||
resolution: {integrity: sha512-l8qFtRqNpCk8xf46VOwhBUva7LBwanoGPJ4KQNwVRl6hmEXStf1BJlfbYRZ+yQpbilbIV6LN+ztX6LaGtyd4TQ==}
|
||||
engines: {node: '>=18.6.0'}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
@@ -5465,8 +5468,8 @@ packages:
|
||||
resolution: {integrity: sha512-TesUflQ0WKZqAvg52PWL6kHgLKP6xB6heTOdoYM0Wt2UHyxNa4K25EZZMgKns3BH1RLVbZCREPpLY0rhnNoHVQ==}
|
||||
engines: {node: ^10 || ^12 || >=14}
|
||||
|
||||
postcss@8.4.42:
|
||||
resolution: {integrity: sha512-hywKUQB9Ra4dR1mGhldy5Aj1X3MWDSIA1cEi+Uy0CjheLvP6Ual5RlwMCh8i/X121yEDLDIKBsrCQ8ba3FDMfQ==}
|
||||
postcss@8.4.44:
|
||||
resolution: {integrity: sha512-Aweb9unOEpQ3ezu4Q00DPvvM2ZTUitJdNKeP/+uQgr1IBIqu574IaZoURId7BKtWMREwzKa9OgzPzezWGPWFQw==}
|
||||
engines: {node: ^10 || ^12 || >=14}
|
||||
|
||||
prelude-ls@1.2.1:
|
||||
@@ -5931,8 +5934,8 @@ packages:
|
||||
shell-quote@1.8.1:
|
||||
resolution: {integrity: sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==}
|
||||
|
||||
shiki@1.15.2:
|
||||
resolution: {integrity: sha512-M+7QZQZiZw/cZeizrC/yryG3eeG8pTUhu7ZaHxVyzPNFIRIlN46YBciquoNPCiXiwLnx6JB62f3lSuSYQrus1w==}
|
||||
shiki@1.16.1:
|
||||
resolution: {integrity: sha512-tCJIMaxDVB1mEIJ5TvfZU7kCPB5eo9fli5+21Olc/bmyv+w8kye3JOp+LZRmGkAyT71hrkefQhTiY+o9mBikRQ==}
|
||||
|
||||
side-channel@1.0.6:
|
||||
resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==}
|
||||
@@ -6581,8 +6584,8 @@ packages:
|
||||
vue:
|
||||
optional: true
|
||||
|
||||
vite-plugin-dts@4.0.3:
|
||||
resolution: {integrity: sha512-+xnTsaONwU2kV6zhRjtbRJSGN41uFR/whqmcb4k4fftLFDJElxthp0PP5Fq8gMeM9ytWMt1yk5gGgekLREWYQQ==}
|
||||
vite-plugin-dts@4.1.0:
|
||||
resolution: {integrity: sha512-sRlmt9k2q8MrX4F2058N3KmB6WyJ3Ao6QaExOv1X99F3j0GhPziEz1zscWQ1q2r1PeFc96L7GIUu8Pl2DPr2Hg==}
|
||||
engines: {node: ^14.18.0 || >=16.0.0}
|
||||
peerDependencies:
|
||||
typescript: '*'
|
||||
@@ -7130,11 +7133,11 @@ snapshots:
|
||||
'@babel/helper-validator-identifier': 7.24.7
|
||||
to-fast-properties: 2.0.0
|
||||
|
||||
'@commitlint/cli@19.4.1(@types/node@22.5.1)(typescript@5.5.4)':
|
||||
'@commitlint/cli@19.4.1(@types/node@22.5.2)(typescript@5.5.4)':
|
||||
dependencies:
|
||||
'@commitlint/format': 19.3.0
|
||||
'@commitlint/lint': 19.4.1
|
||||
'@commitlint/load': 19.4.0(@types/node@22.5.1)(typescript@5.5.4)
|
||||
'@commitlint/load': 19.4.0(@types/node@22.5.2)(typescript@5.5.4)
|
||||
'@commitlint/read': 19.4.0
|
||||
'@commitlint/types': 19.0.3
|
||||
execa: 8.0.1
|
||||
@@ -7181,7 +7184,7 @@ snapshots:
|
||||
'@commitlint/rules': 19.4.1
|
||||
'@commitlint/types': 19.0.3
|
||||
|
||||
'@commitlint/load@19.4.0(@types/node@22.5.1)(typescript@5.5.4)':
|
||||
'@commitlint/load@19.4.0(@types/node@22.5.2)(typescript@5.5.4)':
|
||||
dependencies:
|
||||
'@commitlint/config-validator': 19.0.3
|
||||
'@commitlint/execute-rule': 19.0.0
|
||||
@@ -7189,7 +7192,7 @@ snapshots:
|
||||
'@commitlint/types': 19.0.3
|
||||
chalk: 5.3.0
|
||||
cosmiconfig: 9.0.0(typescript@5.5.4)
|
||||
cosmiconfig-typescript-loader: 5.0.0(@types/node@22.5.1)(cosmiconfig@9.0.0(typescript@5.5.4))(typescript@5.5.4)
|
||||
cosmiconfig-typescript-loader: 5.0.0(@types/node@22.5.2)(cosmiconfig@9.0.0(typescript@5.5.4))(typescript@5.5.4)
|
||||
lodash.isplainobject: 4.0.6
|
||||
lodash.merge: 4.6.2
|
||||
lodash.uniq: 4.5.0
|
||||
@@ -7647,13 +7650,13 @@ snapshots:
|
||||
postcss: 7.0.32
|
||||
purgecss: 2.3.0
|
||||
|
||||
'@generouted/react-router@1.19.6(react-router-dom@6.26.1(react-dom@19.0.0-rc-e948a5ac-20240807(react@19.0.0-rc-e948a5ac-20240807))(react@19.0.0-rc-e948a5ac-20240807))(react@19.0.0-rc-e948a5ac-20240807)(vite@5.4.2(@types/node@22.5.1)(less@4.2.0)(sass@1.77.8)(stylus@0.62.0))':
|
||||
'@generouted/react-router@1.19.6(react-router-dom@6.26.1(react-dom@19.0.0-rc-e948a5ac-20240807(react@19.0.0-rc-e948a5ac-20240807))(react@19.0.0-rc-e948a5ac-20240807))(react@19.0.0-rc-e948a5ac-20240807)(vite@5.4.2(@types/node@22.5.2)(less@4.2.0)(sass@1.77.8)(stylus@0.62.0))':
|
||||
dependencies:
|
||||
fast-glob: 3.3.2
|
||||
generouted: 1.19.6(vite@5.4.2(@types/node@22.5.1)(less@4.2.0)(sass@1.77.8)(stylus@0.62.0))
|
||||
generouted: 1.19.6(vite@5.4.2(@types/node@22.5.2)(less@4.2.0)(sass@1.77.8)(stylus@0.62.0))
|
||||
react: 19.0.0-rc-e948a5ac-20240807
|
||||
react-router-dom: 6.26.1(react-dom@19.0.0-rc-e948a5ac-20240807(react@19.0.0-rc-e948a5ac-20240807))(react@19.0.0-rc-e948a5ac-20240807)
|
||||
vite: 5.4.2(@types/node@22.5.1)(less@4.2.0)(sass@1.77.8)(stylus@0.62.0)
|
||||
vite: 5.4.2(@types/node@22.5.2)(less@4.2.0)(sass@1.77.8)(stylus@0.62.0)
|
||||
|
||||
'@humanwhocodes/config-array@0.11.14':
|
||||
dependencies:
|
||||
@@ -7739,23 +7742,23 @@ snapshots:
|
||||
|
||||
'@material/material-color-utilities@0.3.0': {}
|
||||
|
||||
'@microsoft/api-extractor-model@7.29.4(@types/node@22.5.1)':
|
||||
'@microsoft/api-extractor-model@7.29.4(@types/node@22.5.2)':
|
||||
dependencies:
|
||||
'@microsoft/tsdoc': 0.15.0
|
||||
'@microsoft/tsdoc-config': 0.17.0
|
||||
'@rushstack/node-core-library': 5.5.1(@types/node@22.5.1)
|
||||
'@rushstack/node-core-library': 5.5.1(@types/node@22.5.2)
|
||||
transitivePeerDependencies:
|
||||
- '@types/node'
|
||||
|
||||
'@microsoft/api-extractor@7.47.4(@types/node@22.5.1)':
|
||||
'@microsoft/api-extractor@7.47.4(@types/node@22.5.2)':
|
||||
dependencies:
|
||||
'@microsoft/api-extractor-model': 7.29.4(@types/node@22.5.1)
|
||||
'@microsoft/api-extractor-model': 7.29.4(@types/node@22.5.2)
|
||||
'@microsoft/tsdoc': 0.15.0
|
||||
'@microsoft/tsdoc-config': 0.17.0
|
||||
'@rushstack/node-core-library': 5.5.1(@types/node@22.5.1)
|
||||
'@rushstack/node-core-library': 5.5.1(@types/node@22.5.2)
|
||||
'@rushstack/rig-package': 0.5.3
|
||||
'@rushstack/terminal': 0.13.3(@types/node@22.5.1)
|
||||
'@rushstack/ts-command-line': 4.22.3(@types/node@22.5.1)
|
||||
'@rushstack/terminal': 0.13.3(@types/node@22.5.2)
|
||||
'@rushstack/ts-command-line': 4.22.3(@types/node@22.5.2)
|
||||
lodash: 4.17.21
|
||||
minimatch: 3.0.8
|
||||
resolve: 1.22.8
|
||||
@@ -8311,7 +8314,7 @@ snapshots:
|
||||
'@rollup/rollup-win32-x64-msvc@4.21.0':
|
||||
optional: true
|
||||
|
||||
'@rushstack/node-core-library@5.5.1(@types/node@22.5.1)':
|
||||
'@rushstack/node-core-library@5.5.1(@types/node@22.5.2)':
|
||||
dependencies:
|
||||
ajv: 8.13.0
|
||||
ajv-draft-04: 1.0.0(ajv@8.13.0)
|
||||
@@ -8322,33 +8325,36 @@ snapshots:
|
||||
resolve: 1.22.8
|
||||
semver: 7.5.4
|
||||
optionalDependencies:
|
||||
'@types/node': 22.5.1
|
||||
'@types/node': 22.5.2
|
||||
|
||||
'@rushstack/rig-package@0.5.3':
|
||||
dependencies:
|
||||
resolve: 1.22.8
|
||||
strip-json-comments: 3.1.1
|
||||
|
||||
'@rushstack/terminal@0.13.3(@types/node@22.5.1)':
|
||||
'@rushstack/terminal@0.13.3(@types/node@22.5.2)':
|
||||
dependencies:
|
||||
'@rushstack/node-core-library': 5.5.1(@types/node@22.5.1)
|
||||
'@rushstack/node-core-library': 5.5.1(@types/node@22.5.2)
|
||||
supports-color: 8.1.1
|
||||
optionalDependencies:
|
||||
'@types/node': 22.5.1
|
||||
'@types/node': 22.5.2
|
||||
|
||||
'@rushstack/ts-command-line@4.22.3(@types/node@22.5.1)':
|
||||
'@rushstack/ts-command-line@4.22.3(@types/node@22.5.2)':
|
||||
dependencies:
|
||||
'@rushstack/terminal': 0.13.3(@types/node@22.5.1)
|
||||
'@rushstack/terminal': 0.13.3(@types/node@22.5.2)
|
||||
'@types/argparse': 1.0.38
|
||||
argparse: 1.0.10
|
||||
string-argv: 0.3.2
|
||||
transitivePeerDependencies:
|
||||
- '@types/node'
|
||||
|
||||
'@shikijs/core@1.15.2':
|
||||
'@shikijs/core@1.16.1':
|
||||
dependencies:
|
||||
'@shikijs/vscode-textmate': 9.2.0
|
||||
'@types/hast': 3.0.4
|
||||
|
||||
'@shikijs/vscode-textmate@9.2.0': {}
|
||||
|
||||
'@sindresorhus/is@4.6.0': {}
|
||||
|
||||
'@snyk/github-codeowners@1.1.0':
|
||||
@@ -8600,12 +8606,12 @@ snapshots:
|
||||
dependencies:
|
||||
'@types/http-cache-semantics': 4.0.4
|
||||
'@types/keyv': 3.1.4
|
||||
'@types/node': 22.5.1
|
||||
'@types/node': 22.5.2
|
||||
'@types/responselike': 1.0.3
|
||||
|
||||
'@types/conventional-commits-parser@5.0.0':
|
||||
dependencies:
|
||||
'@types/node': 22.5.1
|
||||
'@types/node': 22.5.2
|
||||
|
||||
'@types/d3-array@3.2.1': {}
|
||||
|
||||
@@ -8741,7 +8747,7 @@ snapshots:
|
||||
'@types/fs-extra@11.0.4':
|
||||
dependencies:
|
||||
'@types/jsonfile': 6.1.4
|
||||
'@types/node': 22.5.1
|
||||
'@types/node': 22.5.2
|
||||
|
||||
'@types/geojson@7946.0.14': {}
|
||||
|
||||
@@ -8757,11 +8763,11 @@ snapshots:
|
||||
|
||||
'@types/jsonfile@6.1.4':
|
||||
dependencies:
|
||||
'@types/node': 22.5.1
|
||||
'@types/node': 22.5.2
|
||||
|
||||
'@types/keyv@3.1.4':
|
||||
dependencies:
|
||||
'@types/node': 22.5.1
|
||||
'@types/node': 22.5.2
|
||||
|
||||
'@types/lodash-es@4.17.12':
|
||||
dependencies:
|
||||
@@ -8781,7 +8787,7 @@ snapshots:
|
||||
dependencies:
|
||||
undici-types: 6.19.6
|
||||
|
||||
'@types/node@22.5.1':
|
||||
'@types/node@22.5.2':
|
||||
dependencies:
|
||||
undici-types: 6.19.6
|
||||
|
||||
@@ -8789,11 +8795,11 @@ snapshots:
|
||||
|
||||
'@types/postcss-modules-local-by-default@4.0.2':
|
||||
dependencies:
|
||||
postcss: 8.4.42
|
||||
postcss: 8.4.44
|
||||
|
||||
'@types/postcss-modules-scope@3.0.4':
|
||||
dependencies:
|
||||
postcss: 8.4.42
|
||||
postcss: 8.4.44
|
||||
|
||||
'@types/prop-types@15.7.12': {}
|
||||
|
||||
@@ -8803,7 +8809,7 @@ snapshots:
|
||||
|
||||
'@types/responselike@1.0.3':
|
||||
dependencies:
|
||||
'@types/node': 22.5.1
|
||||
'@types/node': 22.5.2
|
||||
|
||||
'@types/unist@2.0.10': {}
|
||||
|
||||
@@ -8811,7 +8817,7 @@ snapshots:
|
||||
|
||||
'@types/yauzl@2.10.3':
|
||||
dependencies:
|
||||
'@types/node': 22.5.1
|
||||
'@types/node': 22.5.2
|
||||
optional: true
|
||||
|
||||
'@typescript-eslint/eslint-plugin@8.3.0(@typescript-eslint/parser@8.3.0(eslint@8.57.0)(typescript@5.5.4))(eslint@8.57.0)(typescript@5.5.4)':
|
||||
@@ -8897,21 +8903,21 @@ snapshots:
|
||||
|
||||
'@ungap/structured-clone@1.2.0': {}
|
||||
|
||||
'@vitejs/plugin-react-swc@3.7.0(vite@5.4.2(@types/node@22.5.1)(less@4.2.0)(sass@1.77.8)(stylus@0.62.0))':
|
||||
'@vitejs/plugin-react-swc@3.7.0(vite@5.4.2(@types/node@22.5.2)(less@4.2.0)(sass@1.77.8)(stylus@0.62.0))':
|
||||
dependencies:
|
||||
'@swc/core': 1.6.1
|
||||
vite: 5.4.2(@types/node@22.5.1)(less@4.2.0)(sass@1.77.8)(stylus@0.62.0)
|
||||
vite: 5.4.2(@types/node@22.5.2)(less@4.2.0)(sass@1.77.8)(stylus@0.62.0)
|
||||
transitivePeerDependencies:
|
||||
- '@swc/helpers'
|
||||
|
||||
'@vitejs/plugin-react@4.3.1(vite@5.4.2(@types/node@22.5.1)(less@4.2.0)(sass@1.77.8)(stylus@0.62.0))':
|
||||
'@vitejs/plugin-react@4.3.1(vite@5.4.2(@types/node@22.5.2)(less@4.2.0)(sass@1.77.8)(stylus@0.62.0))':
|
||||
dependencies:
|
||||
'@babel/core': 7.24.5
|
||||
'@babel/plugin-transform-react-jsx-self': 7.24.5(@babel/core@7.24.5)
|
||||
'@babel/plugin-transform-react-jsx-source': 7.24.1(@babel/core@7.24.5)
|
||||
'@types/babel__core': 7.20.5
|
||||
react-refresh: 0.14.2
|
||||
vite: 5.4.2(@types/node@22.5.1)(less@4.2.0)(sass@1.77.8)(stylus@0.62.0)
|
||||
vite: 5.4.2(@types/node@22.5.2)(less@4.2.0)(sass@1.77.8)(stylus@0.62.0)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
@@ -9170,14 +9176,14 @@ snapshots:
|
||||
dependencies:
|
||||
tslib: 2.6.2
|
||||
|
||||
autoprefixer@10.4.20(postcss@8.4.42):
|
||||
autoprefixer@10.4.20(postcss@8.4.44):
|
||||
dependencies:
|
||||
browserslist: 4.23.3
|
||||
caniuse-lite: 1.0.30001646
|
||||
fraction.js: 4.3.7
|
||||
normalize-range: 0.1.2
|
||||
picocolors: 1.0.1
|
||||
postcss: 8.4.42
|
||||
postcss: 8.4.44
|
||||
postcss-value-parser: 4.2.0
|
||||
|
||||
autoprefixer@9.8.8:
|
||||
@@ -9475,9 +9481,9 @@ snapshots:
|
||||
dependencies:
|
||||
toggle-selection: 1.0.6
|
||||
|
||||
cosmiconfig-typescript-loader@5.0.0(@types/node@22.5.1)(cosmiconfig@9.0.0(typescript@5.5.4))(typescript@5.5.4):
|
||||
cosmiconfig-typescript-loader@5.0.0(@types/node@22.5.2)(cosmiconfig@9.0.0(typescript@5.5.4))(typescript@5.5.4):
|
||||
dependencies:
|
||||
'@types/node': 22.5.1
|
||||
'@types/node': 22.5.2
|
||||
cosmiconfig: 9.0.0(typescript@5.5.4)
|
||||
jiti: 1.21.6
|
||||
typescript: 5.5.4
|
||||
@@ -10560,9 +10566,9 @@ snapshots:
|
||||
|
||||
functions-have-names@1.2.3: {}
|
||||
|
||||
generouted@1.19.6(vite@5.4.2(@types/node@22.5.1)(less@4.2.0)(sass@1.77.8)(stylus@0.62.0)):
|
||||
generouted@1.19.6(vite@5.4.2(@types/node@22.5.2)(less@4.2.0)(sass@1.77.8)(stylus@0.62.0)):
|
||||
dependencies:
|
||||
vite: 5.4.2(@types/node@22.5.1)(less@4.2.0)(sass@1.77.8)(stylus@0.62.0)
|
||||
vite: 5.4.2(@types/node@22.5.2)(less@4.2.0)(sass@1.77.8)(stylus@0.62.0)
|
||||
|
||||
gensync@1.0.0-beta.2: {}
|
||||
|
||||
@@ -11173,11 +11179,11 @@ snapshots:
|
||||
|
||||
kind-of@6.0.3: {}
|
||||
|
||||
knip@5.28.0(@types/node@22.5.1)(typescript@5.5.4):
|
||||
knip@5.29.1(@types/node@22.5.2)(typescript@5.5.4):
|
||||
dependencies:
|
||||
'@nodelib/fs.walk': 1.2.8
|
||||
'@snyk/github-codeowners': 1.1.0
|
||||
'@types/node': 22.5.1
|
||||
'@types/node': 22.5.2
|
||||
easy-table: 1.2.0
|
||||
enhanced-resolve: 5.17.1
|
||||
fast-glob: 3.3.2
|
||||
@@ -11996,19 +12002,19 @@ snapshots:
|
||||
dependencies:
|
||||
htmlparser2: 8.0.2
|
||||
js-tokens: 9.0.0
|
||||
postcss: 8.4.42
|
||||
postcss-safe-parser: 6.0.0(postcss@8.4.42)
|
||||
postcss: 8.4.44
|
||||
postcss-safe-parser: 6.0.0(postcss@8.4.44)
|
||||
|
||||
postcss-import@15.1.0(postcss@8.4.42):
|
||||
postcss-import@15.1.0(postcss@8.4.44):
|
||||
dependencies:
|
||||
postcss: 8.4.42
|
||||
postcss: 8.4.44
|
||||
postcss-value-parser: 4.2.0
|
||||
read-cache: 1.0.0
|
||||
resolve: 1.22.8
|
||||
|
||||
postcss-import@16.1.0(postcss@8.4.42):
|
||||
postcss-import@16.1.0(postcss@8.4.44):
|
||||
dependencies:
|
||||
postcss: 8.4.42
|
||||
postcss: 8.4.44
|
||||
postcss-value-parser: 4.2.0
|
||||
read-cache: 1.0.0
|
||||
resolve: 1.22.8
|
||||
@@ -12018,10 +12024,10 @@ snapshots:
|
||||
camelcase-css: 2.0.1
|
||||
postcss: 7.0.39
|
||||
|
||||
postcss-js@4.0.1(postcss@8.4.42):
|
||||
postcss-js@4.0.1(postcss@8.4.44):
|
||||
dependencies:
|
||||
camelcase-css: 2.0.1
|
||||
postcss: 8.4.42
|
||||
postcss: 8.4.44
|
||||
|
||||
postcss-load-config@3.1.4(postcss@8.4.38):
|
||||
dependencies:
|
||||
@@ -12030,12 +12036,12 @@ snapshots:
|
||||
optionalDependencies:
|
||||
postcss: 8.4.38
|
||||
|
||||
postcss-load-config@4.0.2(postcss@8.4.42):
|
||||
postcss-load-config@4.0.2(postcss@8.4.44):
|
||||
dependencies:
|
||||
lilconfig: 3.1.2
|
||||
yaml: 2.5.0
|
||||
optionalDependencies:
|
||||
postcss: 8.4.42
|
||||
postcss: 8.4.44
|
||||
|
||||
postcss-media-query-parser@0.2.3: {}
|
||||
|
||||
@@ -12060,24 +12066,24 @@ snapshots:
|
||||
postcss: 7.0.39
|
||||
postcss-selector-parser: 6.1.2
|
||||
|
||||
postcss-nested@6.0.1(postcss@8.4.42):
|
||||
postcss-nested@6.0.1(postcss@8.4.44):
|
||||
dependencies:
|
||||
postcss: 8.4.42
|
||||
postcss: 8.4.44
|
||||
postcss-selector-parser: 6.1.2
|
||||
|
||||
postcss-resolve-nested-selector@0.1.6: {}
|
||||
|
||||
postcss-safe-parser@6.0.0(postcss@8.4.42):
|
||||
postcss-safe-parser@6.0.0(postcss@8.4.44):
|
||||
dependencies:
|
||||
postcss: 8.4.42
|
||||
postcss: 8.4.44
|
||||
|
||||
postcss-safe-parser@7.0.0(postcss@8.4.42):
|
||||
postcss-safe-parser@7.0.0(postcss@8.4.44):
|
||||
dependencies:
|
||||
postcss: 8.4.42
|
||||
postcss: 8.4.44
|
||||
|
||||
postcss-scss@4.0.9(postcss@8.4.42):
|
||||
postcss-scss@4.0.9(postcss@8.4.44):
|
||||
dependencies:
|
||||
postcss: 8.4.42
|
||||
postcss: 8.4.44
|
||||
|
||||
postcss-selector-parser@6.0.16:
|
||||
dependencies:
|
||||
@@ -12099,9 +12105,9 @@ snapshots:
|
||||
cssesc: 3.0.0
|
||||
util-deprecate: 1.0.2
|
||||
|
||||
postcss-sorting@8.0.2(postcss@8.4.42):
|
||||
postcss-sorting@8.0.2(postcss@8.4.44):
|
||||
dependencies:
|
||||
postcss: 8.4.42
|
||||
postcss: 8.4.44
|
||||
|
||||
postcss-value-parser@3.3.1: {}
|
||||
|
||||
@@ -12136,7 +12142,7 @@ snapshots:
|
||||
picocolors: 1.0.1
|
||||
source-map-js: 1.2.0
|
||||
|
||||
postcss@8.4.42:
|
||||
postcss@8.4.44:
|
||||
dependencies:
|
||||
nanoid: 3.3.7
|
||||
picocolors: 1.0.1
|
||||
@@ -12593,9 +12599,10 @@ snapshots:
|
||||
|
||||
shell-quote@1.8.1: {}
|
||||
|
||||
shiki@1.15.2:
|
||||
shiki@1.16.1:
|
||||
dependencies:
|
||||
'@shikijs/core': 1.15.2
|
||||
'@shikijs/core': 1.16.1
|
||||
'@shikijs/vscode-textmate': 9.2.0
|
||||
'@types/hast': 3.0.4
|
||||
|
||||
side-channel@1.0.6:
|
||||
@@ -12814,8 +12821,8 @@ snapshots:
|
||||
|
||||
stylelint-order@6.0.4(stylelint@16.9.0(typescript@5.5.4)):
|
||||
dependencies:
|
||||
postcss: 8.4.42
|
||||
postcss-sorting: 8.0.2(postcss@8.4.42)
|
||||
postcss: 8.4.44
|
||||
postcss-sorting: 8.0.2(postcss@8.4.44)
|
||||
stylelint: 16.9.0(typescript@5.5.4)
|
||||
|
||||
stylelint-scss@6.5.1(stylelint@16.9.0(typescript@5.5.4)):
|
||||
@@ -12858,9 +12865,9 @@ snapshots:
|
||||
micromatch: 4.0.8
|
||||
normalize-path: 3.0.0
|
||||
picocolors: 1.0.1
|
||||
postcss: 8.4.42
|
||||
postcss: 8.4.44
|
||||
postcss-resolve-nested-selector: 0.1.6
|
||||
postcss-safe-parser: 7.0.0(postcss@8.4.42)
|
||||
postcss-safe-parser: 7.0.0(postcss@8.4.44)
|
||||
postcss-selector-parser: 6.1.2
|
||||
postcss-value-parser: 4.2.0
|
||||
resolve-from: 5.0.0
|
||||
@@ -12999,11 +13006,11 @@ snapshots:
|
||||
normalize-path: 3.0.0
|
||||
object-hash: 3.0.0
|
||||
picocolors: 1.0.1
|
||||
postcss: 8.4.42
|
||||
postcss-import: 15.1.0(postcss@8.4.42)
|
||||
postcss-js: 4.0.1(postcss@8.4.42)
|
||||
postcss-load-config: 4.0.2(postcss@8.4.42)
|
||||
postcss-nested: 6.0.1(postcss@8.4.42)
|
||||
postcss: 8.4.44
|
||||
postcss-import: 15.1.0(postcss@8.4.44)
|
||||
postcss-js: 4.0.1(postcss@8.4.44)
|
||||
postcss-load-config: 4.0.2(postcss@8.4.44)
|
||||
postcss-nested: 6.0.1(postcss@8.4.44)
|
||||
postcss-selector-parser: 6.1.1
|
||||
resolve: 1.22.8
|
||||
sucrase: 3.35.0
|
||||
@@ -13389,9 +13396,9 @@ snapshots:
|
||||
react: 19.0.0-rc-e948a5ac-20240807
|
||||
react-dom: 19.0.0-rc-e948a5ac-20240807(react@19.0.0-rc-e948a5ac-20240807)
|
||||
|
||||
vite-plugin-dts@4.0.3(@types/node@22.5.1)(rollup@4.21.0)(typescript@5.5.4)(vite@5.4.2(@types/node@22.5.1)(less@4.2.0)(sass@1.77.8)(stylus@0.62.0)):
|
||||
vite-plugin-dts@4.1.0(@types/node@22.5.2)(rollup@4.21.0)(typescript@5.5.4)(vite@5.4.2(@types/node@22.5.2)(less@4.2.0)(sass@1.77.8)(stylus@0.62.0)):
|
||||
dependencies:
|
||||
'@microsoft/api-extractor': 7.47.4(@types/node@22.5.1)
|
||||
'@microsoft/api-extractor': 7.47.4(@types/node@22.5.2)
|
||||
'@rollup/pluginutils': 5.1.0(rollup@4.21.0)
|
||||
'@volar/typescript': 2.4.0
|
||||
'@vue/language-core': 2.0.29(typescript@5.5.4)
|
||||
@@ -13403,7 +13410,7 @@ snapshots:
|
||||
typescript: 5.5.4
|
||||
vue-tsc: 2.0.29(typescript@5.5.4)
|
||||
optionalDependencies:
|
||||
vite: 5.4.2(@types/node@22.5.1)(less@4.2.0)(sass@1.77.8)(stylus@0.62.0)
|
||||
vite: 5.4.2(@types/node@22.5.2)(less@4.2.0)(sass@1.77.8)(stylus@0.62.0)
|
||||
transitivePeerDependencies:
|
||||
- '@types/node'
|
||||
- rollup
|
||||
@@ -13414,43 +13421,43 @@ snapshots:
|
||||
esbuild: 0.19.12
|
||||
monaco-editor: 0.51.0
|
||||
|
||||
vite-plugin-sass-dts@1.3.25(postcss@8.4.42)(prettier@3.3.3)(sass@1.77.8)(vite@5.4.2(@types/node@22.5.1)(less@4.2.0)(sass@1.77.8)(stylus@0.62.0)):
|
||||
vite-plugin-sass-dts@1.3.25(postcss@8.4.44)(prettier@3.3.3)(sass@1.77.8)(vite@5.4.2(@types/node@22.5.2)(less@4.2.0)(sass@1.77.8)(stylus@0.62.0)):
|
||||
dependencies:
|
||||
postcss: 8.4.42
|
||||
postcss-js: 4.0.1(postcss@8.4.42)
|
||||
postcss: 8.4.44
|
||||
postcss-js: 4.0.1(postcss@8.4.44)
|
||||
prettier: 3.3.3
|
||||
sass: 1.77.8
|
||||
vite: 5.4.2(@types/node@22.5.1)(less@4.2.0)(sass@1.77.8)(stylus@0.62.0)
|
||||
vite: 5.4.2(@types/node@22.5.2)(less@4.2.0)(sass@1.77.8)(stylus@0.62.0)
|
||||
|
||||
vite-plugin-svgr@4.2.0(rollup@4.21.0)(typescript@5.5.4)(vite@5.4.2(@types/node@22.5.1)(less@4.2.0)(sass@1.77.8)(stylus@0.62.0)):
|
||||
vite-plugin-svgr@4.2.0(rollup@4.21.0)(typescript@5.5.4)(vite@5.4.2(@types/node@22.5.2)(less@4.2.0)(sass@1.77.8)(stylus@0.62.0)):
|
||||
dependencies:
|
||||
'@rollup/pluginutils': 5.1.0(rollup@4.21.0)
|
||||
'@svgr/core': 8.1.0(typescript@5.5.4)
|
||||
'@svgr/plugin-jsx': 8.1.0(@svgr/core@8.1.0(typescript@5.5.4))
|
||||
vite: 5.4.2(@types/node@22.5.1)(less@4.2.0)(sass@1.77.8)(stylus@0.62.0)
|
||||
vite: 5.4.2(@types/node@22.5.2)(less@4.2.0)(sass@1.77.8)(stylus@0.62.0)
|
||||
transitivePeerDependencies:
|
||||
- rollup
|
||||
- supports-color
|
||||
- typescript
|
||||
|
||||
vite-tsconfig-paths@5.0.1(typescript@5.5.4)(vite@5.4.2(@types/node@22.5.1)(less@4.2.0)(sass@1.77.8)(stylus@0.62.0)):
|
||||
vite-tsconfig-paths@5.0.1(typescript@5.5.4)(vite@5.4.2(@types/node@22.5.2)(less@4.2.0)(sass@1.77.8)(stylus@0.62.0)):
|
||||
dependencies:
|
||||
debug: 4.3.6
|
||||
globrex: 0.1.2
|
||||
tsconfck: 3.0.3(typescript@5.5.4)
|
||||
optionalDependencies:
|
||||
vite: 5.4.2(@types/node@22.5.1)(less@4.2.0)(sass@1.77.8)(stylus@0.62.0)
|
||||
vite: 5.4.2(@types/node@22.5.2)(less@4.2.0)(sass@1.77.8)(stylus@0.62.0)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
- typescript
|
||||
|
||||
vite@5.4.2(@types/node@22.5.1)(less@4.2.0)(sass@1.77.8)(stylus@0.62.0):
|
||||
vite@5.4.2(@types/node@22.5.2)(less@4.2.0)(sass@1.77.8)(stylus@0.62.0):
|
||||
dependencies:
|
||||
esbuild: 0.21.5
|
||||
postcss: 8.4.41
|
||||
rollup: 4.21.0
|
||||
optionalDependencies:
|
||||
'@types/node': 22.5.1
|
||||
'@types/node': 22.5.2
|
||||
fsevents: 2.3.3
|
||||
less: 4.2.0
|
||||
sass: 1.77.8
|
||||
|
31
clash-nyanpasu/scripts/generate-git-info.ts
Normal file
31
clash-nyanpasu/scripts/generate-git-info.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import { execSync } from "node:child_process";
|
||||
import fs from "fs-extra";
|
||||
import { GIT_SUMMARY_INFO_PATH, TAURI_APP_TEMP_DIR } from "./utils/env";
|
||||
import { consola } from "./utils/logger";
|
||||
|
||||
async function main() {
|
||||
const [hash, author, time] = execSync(
|
||||
"git show --pretty=format:'%H,%cn,%cI' --no-patch --no-notes",
|
||||
{
|
||||
cwd: process.cwd(),
|
||||
},
|
||||
)
|
||||
.toString()
|
||||
.replace(/'/g, "")
|
||||
.split(",");
|
||||
|
||||
const summary = {
|
||||
hash,
|
||||
author,
|
||||
time,
|
||||
};
|
||||
consola.info(summary);
|
||||
if (!(await fs.exists(TAURI_APP_TEMP_DIR))) {
|
||||
await fs.mkdir(TAURI_APP_TEMP_DIR);
|
||||
}
|
||||
|
||||
await fs.writeJSON(GIT_SUMMARY_INFO_PATH, summary, { spaces: 2 });
|
||||
consola.success("Git summary info generated");
|
||||
}
|
||||
|
||||
main().catch(consola.error);
|
@@ -44,6 +44,8 @@ const resourceFormats = [
|
||||
"amd64.deb",
|
||||
"x64.dmg",
|
||||
"aarch64.dmg",
|
||||
"aarch64.rpm",
|
||||
"arm64.deb",
|
||||
];
|
||||
|
||||
const isValidFormat = (fileName: string): boolean => {
|
||||
|
@@ -7,3 +7,8 @@ export const GITHUB_PROXY = "https://mirror.ghproxy.com/";
|
||||
export const GITHUB_TOKEN = process.env.GITHUB_TOKEN;
|
||||
export const TEMP_DIR = path.join(cwd, "node_modules/.verge");
|
||||
export const MANIFEST_VERSION_PATH = path.join(MANIFEST_DIR, "version.json");
|
||||
export const TAURI_APP_TEMP_DIR = path.join(TAURI_APP_DIR, ".tmp");
|
||||
export const GIT_SUMMARY_INFO_PATH = path.join(
|
||||
TAURI_APP_TEMP_DIR,
|
||||
"git-info.json",
|
||||
);
|
||||
|
@@ -66,7 +66,6 @@ type relayConnImpl struct {
|
||||
// options set those fields
|
||||
l *zap.SugaredLogger
|
||||
remote *lb.Node
|
||||
HandshakeDuration time.Duration
|
||||
RelayLabel string `json:"relay_label"`
|
||||
ConnType string `json:"conn_type"`
|
||||
Options *conf.Options
|
||||
@@ -78,12 +77,6 @@ func WithRelayLabel(relayLabel string) RelayConnOption {
|
||||
}
|
||||
}
|
||||
|
||||
func WithHandshakeDuration(duration time.Duration) RelayConnOption {
|
||||
return func(rci *relayConnImpl) {
|
||||
rci.HandshakeDuration = duration
|
||||
}
|
||||
}
|
||||
|
||||
func WithConnType(connType string) RelayConnOption {
|
||||
return func(rci *relayConnImpl) {
|
||||
rci.ConnType = connType
|
||||
@@ -93,6 +86,7 @@ func WithConnType(connType string) RelayConnOption {
|
||||
func WithRemote(remote *lb.Node) RelayConnOption {
|
||||
return func(rci *relayConnImpl) {
|
||||
rci.remote = remote
|
||||
rci.Stats.HandShakeLatency = remote.HandShakeDuration
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -44,9 +44,6 @@ func newBaseRelayServer(cfg *conf.Config, cmgr cmgr.Cmgr) (*BaseRelayServer, err
|
||||
}
|
||||
|
||||
func (b *BaseRelayServer) RelayTCPConn(ctx context.Context, c net.Conn, remote *lb.Node) error {
|
||||
if remote == nil {
|
||||
remote = b.remotes.Next().Clone()
|
||||
}
|
||||
metrics.CurConnectionCount.WithLabelValues(remote.Label, metrics.METRIC_CONN_TYPE_TCP).Inc()
|
||||
defer metrics.CurConnectionCount.WithLabelValues(remote.Label, metrics.METRIC_CONN_TYPE_TCP).Dec()
|
||||
|
||||
@@ -66,15 +63,11 @@ func (b *BaseRelayServer) RelayTCPConn(ctx context.Context, c net.Conn, remote *
|
||||
return fmt.Errorf("handshake error: %w", err)
|
||||
}
|
||||
defer rc.Close()
|
||||
|
||||
b.l.Infof("RelayTCPConn from %s to %s", c.LocalAddr(), remote.Address)
|
||||
return b.handleRelayConn(c, rc, remote, metrics.METRIC_CONN_TYPE_TCP)
|
||||
}
|
||||
|
||||
func (b *BaseRelayServer) RelayUDPConn(ctx context.Context, c net.Conn, remote *lb.Node) error {
|
||||
if remote == nil {
|
||||
remote = b.remotes.Next().Clone()
|
||||
}
|
||||
metrics.CurConnectionCount.WithLabelValues(remote.Label, metrics.METRIC_CONN_TYPE_UDP).Inc()
|
||||
defer metrics.CurConnectionCount.WithLabelValues(remote.Label, metrics.METRIC_CONN_TYPE_UDP).Dec()
|
||||
|
||||
@@ -144,7 +137,6 @@ func (b *BaseRelayServer) handleRelayConn(c, rc net.Conn, remote *lb.Node, connT
|
||||
conn.WithConnType(connType),
|
||||
conn.WithRelayLabel(b.cfg.Label),
|
||||
conn.WithRelayOptions(b.cfg.Options),
|
||||
conn.WithHandshakeDuration(remote.HandShakeDuration),
|
||||
}
|
||||
relayConn := conn.NewRelayConn(c, rc, opts...)
|
||||
if b.cmgr != nil {
|
||||
|
@@ -99,7 +99,7 @@ func (s *RawServer) ListenAndServe(ctx context.Context) error {
|
||||
}
|
||||
go func(c net.Conn) {
|
||||
defer c.Close()
|
||||
if err := s.RelayTCPConn(ctx, c, nil); err != nil {
|
||||
if err := s.RelayTCPConn(ctx, c, s.remotes.Next()); err != nil {
|
||||
s.l.Errorf("RelayTCPConn meet error: %s", err.Error())
|
||||
}
|
||||
}(c)
|
||||
@@ -118,7 +118,7 @@ func (s *RawServer) listenUDP(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
go func() {
|
||||
if err := s.RelayUDPConn(ctx, c, nil); err != nil {
|
||||
if err := s.RelayUDPConn(ctx, c, s.remotes.Next()); err != nil {
|
||||
s.l.Errorf("RelayUDPConn meet error: %s", err.Error())
|
||||
}
|
||||
}()
|
||||
|
@@ -98,6 +98,8 @@ func (s *WsServer) handleRequest(w http.ResponseWriter, req *http.Request) {
|
||||
var remote *lb.Node
|
||||
if addr := req.URL.Query().Get(conf.WS_QUERY_REMOTE_ADDR); addr != "" {
|
||||
remote = &lb.Node{Address: addr, Label: addr}
|
||||
} else {
|
||||
remote = s.remotes.Next()
|
||||
}
|
||||
|
||||
if req.URL.Query().Get("type") == "udp" {
|
||||
|
393
echo/internal/web/js/metrics.js
Normal file
393
echo/internal/web/js/metrics.js
Normal file
@@ -0,0 +1,393 @@
|
||||
const MetricsModule = (function () {
|
||||
// Constants
|
||||
const API_BASE_URL = '/api/v1';
|
||||
const NODE_METRICS_PATH = '/node_metrics/';
|
||||
const BYTE_TO_MB = 1024 * 1024;
|
||||
|
||||
const handleError = (error) => {
|
||||
console.error('Error:', error);
|
||||
};
|
||||
|
||||
// API functions
|
||||
const fetchData = async (path, params = {}) => {
|
||||
const url = new URL(API_BASE_URL + path, window.location.origin);
|
||||
Object.entries(params).forEach(([key, value]) => url.searchParams.append(key, value));
|
||||
try {
|
||||
const response = await fetch(url.toString());
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
return await response.json();
|
||||
} catch (error) {
|
||||
handleError(error);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const fetchLatestMetric = () => fetchData(NODE_METRICS_PATH, { latest: true }).then((data) => data?.data[0]);
|
||||
const fetchMetrics = (startTs, endTs) => fetchData(NODE_METRICS_PATH, { start_ts: startTs, end_ts: endTs }).then((data) => data?.data);
|
||||
|
||||
// Chart functions
|
||||
const initChart = (canvasId, type, datasets, legendPosition = '', yDisplayText = '', title = '', unit = '') => {
|
||||
const ctx = $(`#${canvasId}`)[0].getContext('2d');
|
||||
const colors = {
|
||||
cpu: 'rgba(255, 99, 132, 1)',
|
||||
memory: 'rgba(54, 162, 235, 1)',
|
||||
disk: 'rgba(255, 206, 86, 1)',
|
||||
receive: 'rgba(0, 150, 255, 1)',
|
||||
transmit: 'rgba(255, 140, 0, 1)',
|
||||
};
|
||||
|
||||
const getDatasetConfig = (label) => {
|
||||
const color = colors[label.toLowerCase()] || 'rgba(0, 0, 0, 1)';
|
||||
return {
|
||||
label,
|
||||
borderColor: color,
|
||||
backgroundColor: color.replace('1)', '0.2)'),
|
||||
borderWidth: 2,
|
||||
pointRadius: 2,
|
||||
pointHoverRadius: 2,
|
||||
fill: true,
|
||||
data: [],
|
||||
};
|
||||
};
|
||||
|
||||
const data = {
|
||||
labels: [],
|
||||
datasets: $.isArray(datasets) ? datasets.map((dataset) => getDatasetConfig(dataset.label)) : [getDatasetConfig(datasets.label)],
|
||||
};
|
||||
|
||||
return new Chart(ctx, {
|
||||
type,
|
||||
data,
|
||||
options: {
|
||||
line: {
|
||||
spanGaps: false, // 设置为 false,不连接空值
|
||||
},
|
||||
responsive: true,
|
||||
plugins: {
|
||||
legend: { position: legendPosition },
|
||||
title: {
|
||||
display: !!title,
|
||||
text: title,
|
||||
position: 'bottom',
|
||||
font: { size: 14, weight: 'bold' },
|
||||
},
|
||||
tooltip: {
|
||||
callbacks: {
|
||||
title: function (tooltipItems) {
|
||||
return new Date(tooltipItems[0].label).toLocaleString();
|
||||
},
|
||||
label: function (context) {
|
||||
let label = context.dataset.label || '';
|
||||
if (label) {
|
||||
label += ': ';
|
||||
}
|
||||
if (context.parsed.y !== null) {
|
||||
label += context.parsed.y.toFixed(2) + ' ' + unit;
|
||||
}
|
||||
return label;
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
type: 'time',
|
||||
time: {
|
||||
unit: 'minute',
|
||||
displayFormats: {
|
||||
minute: 'HH:mm',
|
||||
},
|
||||
},
|
||||
ticks: {
|
||||
maxRotation: 0,
|
||||
autoSkip: true,
|
||||
maxTicksLimit: 10,
|
||||
},
|
||||
adapters: {
|
||||
date: {
|
||||
locale: 'en',
|
||||
},
|
||||
},
|
||||
},
|
||||
y: {
|
||||
beginAtZero: true,
|
||||
title: { display: true, text: yDisplayText, font: { weight: 'bold' } },
|
||||
},
|
||||
},
|
||||
elements: { line: { tension: 0.4 } },
|
||||
downsample: {
|
||||
enabled: true,
|
||||
threshold: 200,
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const updateChart = (chart, newData, labels) => {
|
||||
if (!newData || !labels) {
|
||||
console.error('Invalid data or labels provided');
|
||||
return;
|
||||
}
|
||||
|
||||
if ($.isArray(newData) && $.isArray(newData[0])) {
|
||||
$.each(chart.data.datasets, (index, dataset) => {
|
||||
if (newData[index]) {
|
||||
dataset.data = newData[index].map((value, i) => ({ x: moment(labels[i]), y: value }));
|
||||
}
|
||||
});
|
||||
} else {
|
||||
chart.data.datasets[0].data = newData.map((value, i) => ({ x: moment(labels[i]), y: value }));
|
||||
}
|
||||
|
||||
chart.options.scales.x.min = moment(labels[0]);
|
||||
chart.options.scales.x.max = moment(labels[labels.length - 1]);
|
||||
chart.update();
|
||||
};
|
||||
|
||||
const updateCharts = (charts, metrics, startTs, endTs) => {
|
||||
console.log('Raw metrics data:', metrics);
|
||||
|
||||
const generateTimestamps = (start, end) => {
|
||||
const timestamps = [];
|
||||
let current = moment.unix(start);
|
||||
const endMoment = moment.unix(end);
|
||||
while (current.isSameOrBefore(endMoment)) {
|
||||
timestamps.push(current.toISOString());
|
||||
current.add(1, 'minute');
|
||||
}
|
||||
return timestamps;
|
||||
};
|
||||
|
||||
const timestamps = generateTimestamps(startTs, endTs);
|
||||
|
||||
const processData = (dataKey) => {
|
||||
const data = new Array(timestamps.length).fill(null);
|
||||
metrics.forEach((metric) => {
|
||||
const index = Math.floor((metric.timestamp - startTs) / 60);
|
||||
if (index >= 0 && index < data.length) {
|
||||
data[index] = metric[dataKey];
|
||||
}
|
||||
});
|
||||
return data;
|
||||
};
|
||||
|
||||
updateChart(charts.cpu, processData('cpu_usage'), timestamps);
|
||||
updateChart(charts.memory, processData('memory_usage'), timestamps);
|
||||
updateChart(charts.disk, processData('disk_usage'), timestamps);
|
||||
updateChart(
|
||||
charts.network,
|
||||
[
|
||||
processData('network_in').map((v) => (v === null ? null : v / BYTE_TO_MB)),
|
||||
processData('network_out').map((v) => (v === null ? null : v / BYTE_TO_MB)),
|
||||
],
|
||||
timestamps
|
||||
);
|
||||
};
|
||||
|
||||
const addLatestDataToCharts = (charts, latestMetric) => {
|
||||
console.log('Raw latestMetric data:', latestMetric);
|
||||
const timestamp = moment.unix(latestMetric.timestamp);
|
||||
|
||||
$.each(charts, (key, chart) => {
|
||||
// 检查是否已经有这个时间戳的数据
|
||||
const existingDataIndex = chart.data.labels.findIndex((label) => label.isSame(timestamp));
|
||||
|
||||
if (existingDataIndex === -1) {
|
||||
// 如果是新数据,添加到末尾
|
||||
chart.data.labels.push(timestamp);
|
||||
if (key === 'network') {
|
||||
chart.data.datasets[0].data.push({ x: timestamp, y: latestMetric.network_in / BYTE_TO_MB });
|
||||
chart.data.datasets[1].data.push({ x: timestamp, y: latestMetric.network_out / BYTE_TO_MB });
|
||||
} else {
|
||||
chart.data.datasets[0].data.push({ x: timestamp, y: latestMetric[`${key}_usage`] });
|
||||
}
|
||||
|
||||
// 更新x轴范围,但保持一定的时间窗口
|
||||
const timeWindow = moment.duration(30, 'minutes'); // 设置显示的时间窗口,例如30分钟
|
||||
const oldestAllowedTime = moment(timestamp).subtract(timeWindow);
|
||||
|
||||
chart.options.scales.x.min = oldestAllowedTime;
|
||||
chart.options.scales.x.max = timestamp;
|
||||
|
||||
// 开启图表的平移和缩放功能
|
||||
chart.options.plugins.zoom = {
|
||||
pan: {
|
||||
enabled: true,
|
||||
mode: 'x',
|
||||
},
|
||||
zoom: {
|
||||
wheel: {
|
||||
enabled: true,
|
||||
},
|
||||
pinch: {
|
||||
enabled: true,
|
||||
},
|
||||
mode: 'x',
|
||||
},
|
||||
};
|
||||
|
||||
chart.update();
|
||||
}
|
||||
// 如果数据已存在,我们不做任何操作,保持现有数据
|
||||
});
|
||||
};
|
||||
|
||||
// Chart initialization
|
||||
const initializeCharts = async () => {
|
||||
const metric = await fetchLatestMetric();
|
||||
if (!metric) return null;
|
||||
return {
|
||||
cpu: initChart('cpuChart', 'line', { label: 'CPU' }, 'top', 'Usage (%)', `CPU`, '%'),
|
||||
memory: initChart('memoryChart', 'line', { label: 'Memory' }, 'top', 'Usage (%)', `Memory`, '%'),
|
||||
disk: initChart('diskChart', 'line', { label: 'Disk' }, 'top', 'Usage (%)', `Disk`, '%'),
|
||||
network: initChart(
|
||||
'networkChart',
|
||||
'line',
|
||||
[{ label: 'Receive' }, { label: 'Transmit' }],
|
||||
'top',
|
||||
'Rate (MB/s)',
|
||||
'Network Rate',
|
||||
'MB/s'
|
||||
),
|
||||
};
|
||||
};
|
||||
|
||||
// Date range functions
|
||||
const setupDateRangeDropdown = (charts) => {
|
||||
const $dateRangeDropdown = $('#dateRangeDropdown');
|
||||
const $dateRangeButton = $('#dateRangeButton');
|
||||
const $dateRangeText = $('#dateRangeText');
|
||||
const $dateRangeInput = $('#dateRangeInput');
|
||||
|
||||
$dateRangeDropdown.find('.dropdown-item[data-range]').on('click', function (e) {
|
||||
e.preventDefault();
|
||||
const range = $(this).data('range');
|
||||
const now = new Date();
|
||||
let start, end;
|
||||
switch (range) {
|
||||
case '30m':
|
||||
start = new Date(now - 30 * 60 * 1000);
|
||||
break;
|
||||
case '1h':
|
||||
start = new Date(now - 60 * 60 * 1000);
|
||||
break;
|
||||
case '3h':
|
||||
start = new Date(now - 3 * 60 * 60 * 1000);
|
||||
break;
|
||||
case '6h':
|
||||
start = new Date(now - 6 * 60 * 60 * 1000);
|
||||
break;
|
||||
case '12h':
|
||||
start = new Date(now - 12 * 60 * 60 * 1000);
|
||||
break;
|
||||
case '24h':
|
||||
start = new Date(now - 24 * 60 * 60 * 1000);
|
||||
break;
|
||||
case '7d':
|
||||
start = new Date(now - 7 * 24 * 60 * 60 * 1000);
|
||||
break;
|
||||
}
|
||||
end = now;
|
||||
|
||||
const startTs = Math.floor(start.getTime() / 1000);
|
||||
const endTs = Math.floor(end.getTime() / 1000);
|
||||
fetchDataForRange(charts, startTs, endTs);
|
||||
$dateRangeText.text($(this).text());
|
||||
$dateRangeDropdown.removeClass('is-active');
|
||||
});
|
||||
|
||||
$dateRangeButton.on('click', (event) => {
|
||||
event.stopPropagation();
|
||||
$dateRangeDropdown.toggleClass('is-active');
|
||||
});
|
||||
|
||||
$(document).on('click', (event) => {
|
||||
if (!$dateRangeDropdown.has(event.target).length) {
|
||||
$dateRangeDropdown.removeClass('is-active');
|
||||
}
|
||||
});
|
||||
|
||||
const picker = flatpickr($dateRangeInput[0], {
|
||||
mode: 'range',
|
||||
enableTime: true,
|
||||
dateFormat: 'Y-m-d H:i',
|
||||
onChange: function (selectedDates) {
|
||||
if (selectedDates.length === 2) {
|
||||
const startTs = Math.floor(selectedDates[0].getTime() / 1000);
|
||||
const endTs = Math.floor(selectedDates[1].getTime() / 1000);
|
||||
fetchDataForRange(charts, startTs, endTs);
|
||||
|
||||
const formattedStart = selectedDates[0].toLocaleString();
|
||||
const formattedEnd = selectedDates[1].toLocaleString();
|
||||
$dateRangeText.text(`${formattedStart} - ${formattedEnd}`);
|
||||
|
||||
// 关闭下拉菜单
|
||||
$dateRangeDropdown.removeClass('is-active');
|
||||
}
|
||||
},
|
||||
onClose: function () {
|
||||
// 确保在日期选择器关闭时也关闭下拉菜单
|
||||
$dateRangeDropdown.removeClass('is-active');
|
||||
},
|
||||
});
|
||||
|
||||
// 防止点击日期选择器时关闭下拉菜单
|
||||
$dateRangeInput.on('click', (event) => {
|
||||
event.stopPropagation();
|
||||
});
|
||||
};
|
||||
|
||||
const fetchDataForRange = async (charts, startTs, endTs) => {
|
||||
const metrics = await fetchMetrics(startTs, endTs);
|
||||
if (metrics) {
|
||||
console.log('Raw metrics data:', metrics);
|
||||
updateCharts(charts, metrics, startTs, endTs);
|
||||
}
|
||||
};
|
||||
|
||||
// Auto refresh functions
|
||||
const setupAutoRefresh = (charts) => {
|
||||
let autoRefreshInterval;
|
||||
let isAutoRefreshing = false;
|
||||
$('#refreshButton').click(function () {
|
||||
if (isAutoRefreshing) {
|
||||
clearInterval(autoRefreshInterval);
|
||||
$(this).removeClass('is-info');
|
||||
$(this).find('span:last').text('Auto Refresh');
|
||||
isAutoRefreshing = false;
|
||||
} else {
|
||||
$(this).addClass('is-info');
|
||||
$(this).find('span:last').text('Stop Refresh');
|
||||
isAutoRefreshing = true;
|
||||
refreshData(charts);
|
||||
autoRefreshInterval = setInterval(() => refreshData(charts), 5000);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const refreshData = async (charts) => {
|
||||
const latestMetric = await fetchLatestMetric();
|
||||
if (latestMetric) {
|
||||
addLatestDataToCharts(charts, latestMetric);
|
||||
}
|
||||
};
|
||||
|
||||
// Main initialization function
|
||||
const init = async () => {
|
||||
const charts = await initializeCharts();
|
||||
if (charts) {
|
||||
setupDateRangeDropdown(charts);
|
||||
setupAutoRefresh(charts);
|
||||
}
|
||||
};
|
||||
|
||||
// Public API
|
||||
return {
|
||||
init: init,
|
||||
};
|
||||
})();
|
||||
|
||||
// Initialize when the DOM is ready
|
||||
document.addEventListener('DOMContentLoaded', MetricsModule.init);
|
@@ -22,7 +22,7 @@ import (
|
||||
"github.com/Ehco1996/ehco/internal/metrics"
|
||||
)
|
||||
|
||||
//go:embed templates/*.html
|
||||
//go:embed templates/*.html js/*.js
|
||||
var templatesFS embed.FS
|
||||
|
||||
const (
|
||||
@@ -70,7 +70,7 @@ func NewServer(
|
||||
return nil, errors.Wrap(err, "failed to setup middleware")
|
||||
}
|
||||
|
||||
if err := setupTemplates(e, l); err != nil {
|
||||
if err := setupTemplates(e, l, cfg); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to setup templates")
|
||||
}
|
||||
|
||||
@@ -128,10 +128,13 @@ func setupMiddleware(e *echo.Echo, cfg *config.Config, l *zap.SugaredLogger) err
|
||||
return nil
|
||||
}
|
||||
|
||||
func setupTemplates(e *echo.Echo, l *zap.SugaredLogger) error {
|
||||
func setupTemplates(e *echo.Echo, l *zap.SugaredLogger, cfg *config.Config) error {
|
||||
funcMap := template.FuncMap{
|
||||
"sub": func(a, b int) int { return a - b },
|
||||
"add": func(a, b int) int { return a + b },
|
||||
"CurrentCfg": func() *config.Config {
|
||||
return cfg
|
||||
},
|
||||
}
|
||||
tmpl, err := template.New("").Funcs(funcMap).ParseFS(templatesFS, "templates/*.html")
|
||||
if err != nil {
|
||||
@@ -158,6 +161,7 @@ func setupMetrics(cfg *config.Config) error {
|
||||
func setupRoutes(s *Server) {
|
||||
e := s.e
|
||||
|
||||
e.StaticFS("/js", echo.MustSubFS(templatesFS, "js"))
|
||||
e.GET(metricsPath, echo.WrapHandler(promhttp.Handler()))
|
||||
e.GET("/debug/pprof/*", echo.WrapHandler(http.DefaultServeMux))
|
||||
|
||||
|
@@ -1,5 +1,5 @@
|
||||
<head>
|
||||
<title>Ehco Web</title>
|
||||
<title>Ehco Web({{ (CurrentCfg).NodeLabel}})</title>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="description" content="ehco web" />
|
||||
<meta name="keywords" content="ehco-relay" />
|
||||
@@ -7,7 +7,9 @@
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/1.0.1/css/bulma.min.css" />
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css" />
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/flatpickr/4.6.13/flatpickr.min.css" />
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/flatpickr/4.6.13/flatpickr.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.4.1/chart.umd.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.4/moment.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/chartjs-adapter-moment/1.0.1/chartjs-adapter-moment.min.js"></script>
|
||||
</head>
|
||||
|
@@ -52,323 +52,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
$(document).ready(async function () {
|
||||
// Constants
|
||||
const API_BASE_URL = '/api/v1';
|
||||
const NODE_METRICS_PATH = '/node_metrics/';
|
||||
const BYTE_TO_MB = 1024 * 1024;
|
||||
const BYTE_TO_GB = BYTE_TO_MB * 1024;
|
||||
|
||||
// Utility functions
|
||||
const handleError = (error) => {
|
||||
console.error('Error:', error);
|
||||
// You can add user notifications here
|
||||
};
|
||||
|
||||
const formatDate = (timeStamp) => {
|
||||
const date = new Date(timeStamp * 1000);
|
||||
return date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit' });
|
||||
};
|
||||
|
||||
const formatBytes = (bytes, decimals = 2) => {
|
||||
return (bytes / BYTE_TO_GB).toFixed(decimals);
|
||||
};
|
||||
|
||||
// API functions
|
||||
const fetchData = async (path, params = {}) => {
|
||||
const url = new URL(API_BASE_URL + path, window.location.origin);
|
||||
$.each(params, (key, value) => url.searchParams.append(key, value));
|
||||
try {
|
||||
const response = await $.ajax({
|
||||
url: url.toString(),
|
||||
method: 'GET',
|
||||
dataType: 'json',
|
||||
});
|
||||
return response;
|
||||
} catch (error) {
|
||||
handleError(error);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const fetchLatestMetric = () => fetchData(NODE_METRICS_PATH, { latest: true }).then((data) => data?.data[0]);
|
||||
const fetchMetrics = (startTs, endTs) =>
|
||||
fetchData(NODE_METRICS_PATH, { start_ts: startTs, end_ts: endTs }).then((data) => data?.data);
|
||||
|
||||
// Chart functions
|
||||
const initChart = (canvasId, type, datasets, legendPosition = '', yDisplayText = '', title = '', unit = '') => {
|
||||
const ctx = $(`#${canvasId}`)[0].getContext('2d');
|
||||
const colors = {
|
||||
cpu: 'rgba(255, 99, 132, 1)',
|
||||
memory: 'rgba(54, 162, 235, 1)',
|
||||
disk: 'rgba(255, 206, 86, 1)',
|
||||
receive: 'rgba(0, 150, 255, 1)',
|
||||
transmit: 'rgba(255, 140, 0, 1)',
|
||||
};
|
||||
|
||||
const getDatasetConfig = (label) => {
|
||||
const color = colors[label.toLowerCase()] || 'rgba(0, 0, 0, 1)'; // 默认颜色
|
||||
console.log(label, color);
|
||||
return {
|
||||
label,
|
||||
borderColor: color,
|
||||
backgroundColor: color.replace('1)', '0.2)'),
|
||||
borderWidth: 2,
|
||||
pointRadius: 0,
|
||||
fill: true,
|
||||
data: [],
|
||||
};
|
||||
};
|
||||
|
||||
const data = {
|
||||
labels: [],
|
||||
datasets: $.isArray(datasets)
|
||||
? datasets.map((dataset) => getDatasetConfig(dataset.label))
|
||||
: [getDatasetConfig(datasets.label)],
|
||||
};
|
||||
|
||||
return new Chart(ctx, {
|
||||
type,
|
||||
data,
|
||||
options: {
|
||||
responsive: true,
|
||||
plugins: {
|
||||
legend: { position: legendPosition },
|
||||
title: {
|
||||
display: !!title,
|
||||
text: title,
|
||||
position: 'bottom',
|
||||
font: { size: 14, weight: 'bold' },
|
||||
},
|
||||
tooltip: {
|
||||
callbacks: {
|
||||
label: function (context) {
|
||||
let label = context.dataset.label || '';
|
||||
if (label) {
|
||||
label += ': ';
|
||||
}
|
||||
if (context.parsed.y !== null) {
|
||||
label += context.parsed.y.toFixed(2) + ' ' + unit;
|
||||
}
|
||||
return label;
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
scales: {
|
||||
y: {
|
||||
beginAtZero: true,
|
||||
title: { display: true, text: yDisplayText, font: { weight: 'bold' } },
|
||||
},
|
||||
x: {
|
||||
ticks: { maxRotation: 0, autoSkip: true, maxTicksLimit: 10 },
|
||||
},
|
||||
},
|
||||
elements: { line: { tension: 0.4 } },
|
||||
downsample: {
|
||||
enabled: true,
|
||||
threshold: 200,
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const updateChart = (chart, newData, labels) => {
|
||||
if (!newData || !labels) {
|
||||
console.error('Invalid data or labels provided');
|
||||
return;
|
||||
}
|
||||
|
||||
const formattedLabels = labels.map(formatDate);
|
||||
|
||||
if ($.isArray(newData) && $.isArray(newData[0])) {
|
||||
$.each(chart.data.datasets, (index, dataset) => {
|
||||
if (newData[index]) {
|
||||
dataset.data = newData[index];
|
||||
}
|
||||
});
|
||||
} else {
|
||||
chart.data.datasets[0].data = newData;
|
||||
}
|
||||
|
||||
chart.data.labels = formattedLabels;
|
||||
chart.update();
|
||||
};
|
||||
|
||||
const updateCharts = (charts, metrics) => {
|
||||
console.log('Raw metrics data:', metrics);
|
||||
const timestamps = metrics.map((data) => data.timestamp);
|
||||
updateChart(
|
||||
charts.cpu,
|
||||
metrics.map((data) => data.cpu_usage),
|
||||
timestamps,
|
||||
);
|
||||
updateChart(
|
||||
charts.memory,
|
||||
metrics.map((data) => data.memory_usage),
|
||||
timestamps,
|
||||
);
|
||||
updateChart(
|
||||
charts.disk,
|
||||
metrics.map((data) => data.disk_usage),
|
||||
timestamps,
|
||||
);
|
||||
updateChart(
|
||||
charts.network,
|
||||
[metrics.map((data) => data.network_in / BYTE_TO_MB), metrics.map((data) => data.network_out / BYTE_TO_MB)],
|
||||
timestamps,
|
||||
);
|
||||
};
|
||||
|
||||
const addLatestDataToCharts = (charts, latestMetric) => {
|
||||
console.log('Raw latestMetric data:', latestMetric);
|
||||
const timestamp = formatDate(latestMetric.timestamp);
|
||||
$.each(charts, (key, chart) => {
|
||||
chart.data.labels.push(timestamp);
|
||||
if (key === 'network') {
|
||||
chart.data.datasets[0].data.push(latestMetric.network_in / BYTE_TO_MB);
|
||||
chart.data.datasets[1].data.push(latestMetric.network_out / BYTE_TO_MB);
|
||||
} else {
|
||||
chart.data.datasets[0].data.push(latestMetric[`${key}_usage`]);
|
||||
}
|
||||
chart.update();
|
||||
});
|
||||
};
|
||||
|
||||
// Chart initialization
|
||||
const initializeCharts = async () => {
|
||||
const metric = await fetchLatestMetric();
|
||||
if (!metric) return null;
|
||||
return {
|
||||
cpu: initChart('cpuChart', 'line', { label: 'CPU' }, 'top', 'Usage (%)', `CPU`, '%'),
|
||||
memory: initChart('memoryChart', 'line', { label: 'Memory' }, 'top', 'Usage (%)', `Memory`, '%'),
|
||||
disk: initChart('diskChart', 'line', { label: 'Disk' }, 'top', 'Usage (%)', `Disk`, '%'),
|
||||
network: initChart(
|
||||
'networkChart',
|
||||
'line',
|
||||
[{ label: 'Receive' }, { label: 'Transmit' }],
|
||||
'top',
|
||||
'Rate (MB/s)',
|
||||
'Network Rate',
|
||||
'MB/s',
|
||||
),
|
||||
};
|
||||
};
|
||||
|
||||
// Date range functions
|
||||
const setupDateRangeDropdown = (charts) => {
|
||||
const $dateRangeDropdown = $('#dateRangeDropdown');
|
||||
const $dateRangeButton = $('#dateRangeButton');
|
||||
const $dateRangeText = $('#dateRangeText');
|
||||
const $dateRangeInput = $('#dateRangeInput');
|
||||
|
||||
$dateRangeDropdown.find('.dropdown-item[data-range]').on('click', function (e) {
|
||||
e.preventDefault();
|
||||
const range = $(this).data('range');
|
||||
const now = new Date();
|
||||
let start, end;
|
||||
switch (range) {
|
||||
case '30m':
|
||||
start = new Date(now - 30 * 60 * 1000);
|
||||
break;
|
||||
case '1h':
|
||||
start = new Date(now - 60 * 60 * 1000);
|
||||
break;
|
||||
case '3h':
|
||||
start = new Date(now - 3 * 60 * 60 * 1000);
|
||||
break;
|
||||
case '6h':
|
||||
start = new Date(now - 6 * 60 * 60 * 1000);
|
||||
break;
|
||||
case '12h':
|
||||
start = new Date(now - 12 * 60 * 60 * 1000);
|
||||
break;
|
||||
case '24h':
|
||||
start = new Date(now - 24 * 60 * 60 * 1000);
|
||||
break;
|
||||
case '7d':
|
||||
start = new Date(now - 7 * 24 * 60 * 60 * 1000);
|
||||
break;
|
||||
}
|
||||
end = now;
|
||||
|
||||
const startTs = Math.floor(start.getTime() / 1000);
|
||||
const endTs = Math.floor(end.getTime() / 1000);
|
||||
fetchDataForRange(charts, startTs, endTs);
|
||||
$dateRangeText.text($(this).text());
|
||||
$dateRangeDropdown.removeClass('is-active');
|
||||
});
|
||||
|
||||
$dateRangeButton.on('click', (event) => {
|
||||
event.stopPropagation();
|
||||
$dateRangeDropdown.toggleClass('is-active');
|
||||
});
|
||||
|
||||
$(document).on('click', (event) => {
|
||||
if (!$dateRangeDropdown.has(event.target).length) {
|
||||
$dateRangeDropdown.removeClass('is-active');
|
||||
}
|
||||
});
|
||||
|
||||
const picker = flatpickr($dateRangeInput[0], {
|
||||
mode: 'range',
|
||||
enableTime: true,
|
||||
dateFormat: 'Y-m-d H:i',
|
||||
onChange: function (selectedDates) {
|
||||
if (selectedDates.length === 2) {
|
||||
const startTs = Math.floor(selectedDates[0].getTime() / 1000);
|
||||
const endTs = Math.floor(selectedDates[1].getTime() / 1000);
|
||||
fetchDataForRange(charts, startTs, endTs);
|
||||
|
||||
const formattedStart = selectedDates[0].toLocaleString();
|
||||
const formattedEnd = selectedDates[1].toLocaleString();
|
||||
$dateRangeText.text(`${formattedStart} - ${formattedEnd}`);
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
const fetchDataForRange = async (charts, startTs, endTs) => {
|
||||
const metrics = await fetchMetrics(startTs, endTs);
|
||||
if (metrics) {
|
||||
console.log('Raw metrics data:', metrics);
|
||||
updateCharts(charts, metrics);
|
||||
}
|
||||
};
|
||||
|
||||
// Auto refresh functions
|
||||
const setupAutoRefresh = (charts) => {
|
||||
let autoRefreshInterval;
|
||||
let isAutoRefreshing = false;
|
||||
$('#refreshButton').click(function () {
|
||||
if (isAutoRefreshing) {
|
||||
clearInterval(autoRefreshInterval);
|
||||
$(this).removeClass('is-info');
|
||||
$(this).find('span:last').text('Auto Refresh');
|
||||
isAutoRefreshing = false;
|
||||
} else {
|
||||
$(this).addClass('is-info');
|
||||
$(this).find('span:last').text('Stop Refresh');
|
||||
isAutoRefreshing = true;
|
||||
refreshData(charts);
|
||||
autoRefreshInterval = setInterval(() => refreshData(charts), 5000);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const refreshData = async (charts) => {
|
||||
const latestMetric = await fetchLatestMetric();
|
||||
if (latestMetric) {
|
||||
addLatestDataToCharts(charts, latestMetric);
|
||||
}
|
||||
};
|
||||
|
||||
// Main execution
|
||||
const charts = await initializeCharts();
|
||||
if (charts) {
|
||||
setupDateRangeDropdown(charts);
|
||||
setupAutoRefresh(charts);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<!-- </div> -->
|
||||
<script src="js/metrics.js"></script>
|
||||
</div>
|
||||
|
@@ -4,7 +4,7 @@
|
||||
<a class="navbar-item" href="/">
|
||||
<h1 class="title is-4">Ehco Relay</h1>
|
||||
</a>
|
||||
<a role="button" class="navbar-burger" aria-label="menu" aria-expanded="false">
|
||||
<a role="button" class="navbar-burger" aria-label="menu" aria-expanded="false" data-target="navbarMenu">
|
||||
<span aria-hidden="true"></span>
|
||||
<span aria-hidden="true"></span>
|
||||
<span aria-hidden="true"></span>
|
||||
@@ -12,7 +12,7 @@
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="navbar-menu">
|
||||
<div id="navbarMenu" class="navbar-menu">
|
||||
<div class="navbar-start">
|
||||
<a href="/rules/" class="navbar-item">
|
||||
<span class="icon"><i class="fas fa-list"></i></span>
|
||||
@@ -56,3 +56,28 @@
|
||||
</div>
|
||||
</nav>
|
||||
<hr />
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
// Get all "navbar-burger" elements
|
||||
const $navbarBurgers = Array.prototype.slice.call(document.querySelectorAll('.navbar-burger'), 0);
|
||||
|
||||
// Add a click event on each of them
|
||||
$navbarBurgers.forEach((el) => {
|
||||
el.addEventListener('click', () => {
|
||||
// Get the target from the "data-target" attribute
|
||||
const target = el.dataset.target;
|
||||
const $target = document.getElementById(target);
|
||||
|
||||
// Check if the target element exists
|
||||
if ($target) {
|
||||
// Toggle the "is-active" class on both the "navbar-burger" and the "navbar-menu"
|
||||
el.classList.toggle('is-active');
|
||||
$target.classList.toggle('is-active');
|
||||
} else {
|
||||
console.error(`Target element with id "${target}" not found`);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
@@ -10,7 +10,7 @@
|
||||
dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3328-roc-cc.dtb
|
||||
--- /dev/null
|
||||
+++ b/arch/arm64/boot/dts/rockchip/rk3328-orangepi-r1-plus-lts.dts
|
||||
@@ -0,0 +1,71 @@
|
||||
@@ -0,0 +1,73 @@
|
||||
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
|
||||
+/*
|
||||
+ * Copyright (c) 2016 Xunlong Software. Co., Ltd.
|
||||
@@ -54,9 +54,11 @@
|
||||
+ "ethernet-phy-ieee802.3-c22";
|
||||
+ reg = <0>;
|
||||
+
|
||||
+ motorcomm,auto-sleep-disabled;
|
||||
+ motorcomm,clk-out-frequency-hz = <125000000>;
|
||||
+ motorcomm,keep-pll-enabled;
|
||||
+ motorcomm,auto-sleep-disabled;
|
||||
+ motorcomm,rx-clk-drv-microamp = <5020>;
|
||||
+ motorcomm,rx-data-drv-microamp = <5020>;
|
||||
+
|
||||
+ pinctrl-0 = <ð_phy_reset_pin>;
|
||||
+ pinctrl-names = "default";
|
||||
|
@@ -2,7 +2,6 @@ package updater
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
@@ -12,23 +11,26 @@ import (
|
||||
"sync"
|
||||
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
"github.com/metacubex/mihomo/log"
|
||||
)
|
||||
|
||||
var (
|
||||
ExternalUIURL string
|
||||
ExternalUIPath string
|
||||
ExternalUIFolder string
|
||||
ExternalUIName string
|
||||
)
|
||||
var (
|
||||
ErrIncompleteConf = errors.New("ExternalUI configure incomplete")
|
||||
AutoUpdateUI bool
|
||||
)
|
||||
|
||||
var xdMutex sync.Mutex
|
||||
|
||||
func UpdateUI() error {
|
||||
xdMutex.Lock()
|
||||
defer xdMutex.Unlock()
|
||||
|
||||
err := prepareUIPath()
|
||||
if err != nil {
|
||||
return fmt.Errorf("prepare UI path failed: %w", err)
|
||||
}
|
||||
|
||||
data, err := downloadForBytes(ExternalUIURL)
|
||||
if err != nil {
|
||||
return fmt.Errorf("can't download file: %w", err)
|
||||
@@ -40,7 +42,7 @@ func UpdateUI() error {
|
||||
}
|
||||
defer os.Remove(saved)
|
||||
|
||||
err = cleanup(ExternalUIFolder)
|
||||
err = cleanup(ExternalUIPath)
|
||||
if err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
return fmt.Errorf("cleanup exist file error: %w", err)
|
||||
@@ -52,28 +54,20 @@ func UpdateUI() error {
|
||||
return fmt.Errorf("can't extract zip file: %w", err)
|
||||
}
|
||||
|
||||
err = os.Rename(unzipFolder, ExternalUIFolder)
|
||||
err = os.Rename(unzipFolder, ExternalUIPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("can't rename folder: %w", err)
|
||||
return fmt.Errorf("rename UI folder failed: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func PrepareUIPath() error {
|
||||
if ExternalUIPath == "" || ExternalUIURL == "" {
|
||||
return ErrIncompleteConf
|
||||
}
|
||||
|
||||
if ExternalUIName != "" {
|
||||
ExternalUIFolder = filepath.Clean(path.Join(ExternalUIPath, ExternalUIName))
|
||||
func prepareUIPath() error {
|
||||
if _, err := os.Stat(ExternalUIPath); os.IsNotExist(err) {
|
||||
log.Infoln("dir %s does not exist, creating", ExternalUIPath)
|
||||
if err := os.MkdirAll(ExternalUIPath, os.ModePerm); err != nil {
|
||||
return err
|
||||
log.Warnln("create dir %s error: %s", ExternalUIPath, err)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ExternalUIFolder = ExternalUIPath
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@@ -7,7 +7,6 @@ import (
|
||||
"net"
|
||||
"net/netip"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"regexp"
|
||||
"strings"
|
||||
@@ -704,33 +703,23 @@ func parseGeneral(cfg *RawConfig) (*General, error) {
|
||||
}
|
||||
N.DisableKeepAlive = cfg.DisableKeepAlive
|
||||
|
||||
updater.ExternalUIPath = cfg.ExternalUI
|
||||
// checkout externalUI exist
|
||||
if updater.ExternalUIPath != "" {
|
||||
updater.ExternalUIPath = C.Path.Resolve(updater.ExternalUIPath)
|
||||
if _, err := os.Stat(updater.ExternalUIPath); os.IsNotExist(err) {
|
||||
defaultUIpath := path.Join(C.Path.HomeDir(), "ui")
|
||||
log.Warnln("external-ui: %s does not exist, creating folder in %s", updater.ExternalUIPath, defaultUIpath)
|
||||
if err := os.MkdirAll(defaultUIpath, os.ModePerm); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
updater.ExternalUIPath = defaultUIpath
|
||||
cfg.ExternalUI = defaultUIpath
|
||||
}
|
||||
}
|
||||
// checkout UIpath/name exist
|
||||
if cfg.ExternalUIName != "" {
|
||||
updater.ExternalUIName = cfg.ExternalUIName
|
||||
if cfg.ExternalUI != "" {
|
||||
updater.AutoUpdateUI = true
|
||||
updater.ExternalUIPath = C.Path.Resolve(cfg.ExternalUI)
|
||||
} else {
|
||||
updater.ExternalUIFolder = updater.ExternalUIPath
|
||||
}
|
||||
if cfg.ExternalUIURL != "" {
|
||||
updater.ExternalUIURL = cfg.ExternalUIURL
|
||||
// default externalUI path
|
||||
updater.ExternalUIPath = path.Join(C.Path.HomeDir(), "ui")
|
||||
}
|
||||
|
||||
err := updater.PrepareUIPath()
|
||||
if err != nil {
|
||||
log.Errorln("PrepareUIPath error: %s", err)
|
||||
// checkout UIpath/name exist
|
||||
if cfg.ExternalUIName != "" {
|
||||
updater.AutoUpdateUI = true
|
||||
updater.ExternalUIPath = path.Join(updater.ExternalUIPath, cfg.ExternalUIName)
|
||||
}
|
||||
|
||||
if cfg.ExternalUIURL != "" {
|
||||
updater.ExternalUIURL = cfg.ExternalUIURL
|
||||
}
|
||||
|
||||
return &General{
|
||||
|
@@ -381,12 +381,12 @@ func updateTunnels(tunnels []LC.Tunnel) {
|
||||
}
|
||||
|
||||
func initExternalUI() {
|
||||
if updater.ExternalUIFolder != "" {
|
||||
dirEntries, _ := os.ReadDir(updater.ExternalUIFolder)
|
||||
if updater.AutoUpdateUI {
|
||||
dirEntries, _ := os.ReadDir(updater.ExternalUIPath)
|
||||
if len(dirEntries) > 0 {
|
||||
log.Infoln("UI already exists")
|
||||
log.Infoln("UI already exists, skip downloading")
|
||||
} else {
|
||||
log.Infoln("UI not exists, downloading")
|
||||
log.Infoln("External UI downloading ...")
|
||||
updater.UpdateUI()
|
||||
}
|
||||
}
|
||||
|
@@ -60,9 +60,15 @@ func getGroupDelay(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if proxy.(*adapter.Proxy).Type() == C.URLTest {
|
||||
URLTestGroup := proxy.(*adapter.Proxy).ProxyAdapter.(*outboundgroup.URLTest)
|
||||
URLTestGroup.ForceSet("")
|
||||
switch proxy.(*adapter.Proxy).Type() {
|
||||
case C.URLTest:
|
||||
if urlTestGroup, ok := proxy.(*adapter.Proxy).ProxyAdapter.(*outboundgroup.URLTest); ok {
|
||||
urlTestGroup.ForceSet("")
|
||||
}
|
||||
case C.Fallback:
|
||||
if fallbackGroup, ok := proxy.(*adapter.Proxy).ProxyAdapter.(*outboundgroup.Fallback); ok {
|
||||
fallbackGroup.ForceSet("")
|
||||
}
|
||||
}
|
||||
|
||||
if proxy.(*adapter.Proxy).Type() != C.Selector {
|
||||
|
@@ -1,7 +1,6 @@
|
||||
package route
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
@@ -50,15 +49,9 @@ func upgradeCore(w http.ResponseWriter, r *http.Request) {
|
||||
func updateUI(w http.ResponseWriter, r *http.Request) {
|
||||
err := updater.UpdateUI()
|
||||
if err != nil {
|
||||
if errors.Is(err, updater.ErrIncompleteConf) {
|
||||
log.Warnln("%s", err)
|
||||
render.Status(r, http.StatusNotImplemented)
|
||||
render.JSON(w, r, newError(fmt.Sprintf("%s", err)))
|
||||
} else {
|
||||
log.Warnln("%s", err)
|
||||
render.Status(r, http.StatusInternalServerError)
|
||||
render.JSON(w, r, newError(fmt.Sprintf("%s", err)))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
@@ -328,6 +328,33 @@ lua_api() {
|
||||
echo $(lua -e "local api = require 'luci.passwall.api' print(api.${func})")
|
||||
}
|
||||
|
||||
parse_doh() {
|
||||
local __doh=$1 __url_var=$2 __host_var=$3 __port_var=$4 __bootstrap_var=$5
|
||||
__doh=$(echo -e "$__doh" | tr -d ' \t\n')
|
||||
local __url=${__doh%%,*}
|
||||
local __bootstrap=${__doh#*,}
|
||||
local __host_port=$(lua_api "get_domain_from_url(\"${__url}\")")
|
||||
local __host __port
|
||||
if echo "${__host_port}" | grep -q '^\[.*\]:[0-9]\+$'; then
|
||||
__host=${__host_port%%]:*}]
|
||||
__port=${__host_port##*:}
|
||||
elif echo "${__host_port}" | grep -q ':[0-9]\+$'; then
|
||||
__host=${__host_port%:*}
|
||||
__port=${__host_port##*:}
|
||||
else
|
||||
__host=${__host_port}
|
||||
__port=443
|
||||
fi
|
||||
__host=${__host#[}
|
||||
__host=${__host%]}
|
||||
if [ "$(lua_api "is_ip(\"${__host}\")")" = "true" ]; then
|
||||
__bootstrap=${__host}
|
||||
fi
|
||||
__bootstrap=${__bootstrap#[}
|
||||
__bootstrap=${__bootstrap%]}
|
||||
eval "${__url_var}='${__url}' ${__host_var}='${__host}' ${__port_var}='${__port}' ${__bootstrap_var}='${__bootstrap}'"
|
||||
}
|
||||
|
||||
run_ipt2socks() {
|
||||
local flag proto tcp_tproxy local_port socks_address socks_port socks_username socks_password log_file
|
||||
local _extra_param=""
|
||||
@@ -422,15 +449,8 @@ run_singbox() {
|
||||
_extra_param="${_extra_param} -remote_dns_server ${_dns_address} -remote_dns_port ${_dns_port} -remote_dns_tcp_server tcp://${_dns}"
|
||||
;;
|
||||
doh)
|
||||
local _doh_url=$(echo $remote_dns_doh | awk -F ',' '{print $1}')
|
||||
local _doh_host_port=$(lua_api "get_domain_from_url(\"${_doh_url}\")")
|
||||
#local _doh_host_port=$(echo $_doh_url | sed "s/https:\/\///g" | awk -F '/' '{print $1}')
|
||||
local _doh_host=$(echo $_doh_host_port | awk -F ':' '{print $1}')
|
||||
local is_ip=$(lua_api "is_ip(\"${_doh_host}\")")
|
||||
local _doh_port=$(echo $_doh_host_port | awk -F ':' '{print $2}')
|
||||
[ -z "${_doh_port}" ] && _doh_port=443
|
||||
local _doh_bootstrap=$(echo $remote_dns_doh | cut -d ',' -sf 2-)
|
||||
[ "${is_ip}" = "true" ] && _doh_bootstrap=${_doh_host}
|
||||
local _doh_url _doh_host _doh_port _doh_bootstrap
|
||||
parse_doh "$remote_dns_doh" _doh_url _doh_host _doh_port _doh_bootstrap
|
||||
[ -n "$_doh_bootstrap" ] && _extra_param="${_extra_param} -remote_dns_server ${_doh_bootstrap}"
|
||||
_extra_param="${_extra_param} -remote_dns_port ${_doh_port} -remote_dns_doh_url ${_doh_url} -remote_dns_doh_host ${_doh_host}"
|
||||
;;
|
||||
@@ -481,15 +501,8 @@ run_xray() {
|
||||
_extra_param="${_extra_param} -remote_dns_tcp_server ${_dns_address} -remote_dns_tcp_port ${_dns_port}"
|
||||
}
|
||||
[ -n "${remote_dns_doh}" ] && {
|
||||
local _doh_url=$(echo $remote_dns_doh | awk -F ',' '{print $1}')
|
||||
local _doh_host_port=$(lua_api "get_domain_from_url(\"${_doh_url}\")")
|
||||
#local _doh_host_port=$(echo $_doh_url | sed "s/https:\/\///g" | awk -F '/' '{print $1}')
|
||||
local _doh_host=$(echo $_doh_host_port | awk -F ':' '{print $1}')
|
||||
local is_ip=$(lua_api "is_ip(\"${_doh_host}\")")
|
||||
local _doh_port=$(echo $_doh_host_port | awk -F ':' '{print $2}')
|
||||
[ -z "${_doh_port}" ] && _doh_port=443
|
||||
local _doh_bootstrap=$(echo $remote_dns_doh | cut -d ',' -sf 2-)
|
||||
[ "${is_ip}" = "true" ] && _doh_bootstrap=${_doh_host}
|
||||
local _doh_url _doh_host _doh_port _doh_bootstrap
|
||||
parse_doh "$remote_dns_doh" _doh_url _doh_host _doh_port _doh_bootstrap
|
||||
[ -n "$_doh_bootstrap" ] && _extra_param="${_extra_param} -remote_dns_doh_ip ${_doh_bootstrap}"
|
||||
_extra_param="${_extra_param} -remote_dns_doh_port ${_doh_port} -remote_dns_doh_url ${_doh_url} -remote_dns_doh_host ${_doh_host}"
|
||||
}
|
||||
@@ -1436,16 +1449,9 @@ start_dns() {
|
||||
_args="${_args} remote_dns_doh=${remote_dns_doh}"
|
||||
echolog " - Sing-Box DNS(${TUN_DNS}) -> ${remote_dns_doh}"
|
||||
|
||||
local _doh_url=$(echo $remote_dns_doh | awk -F ',' '{print $1}')
|
||||
local _doh_host_port=$(lua_api "get_domain_from_url(\"${_doh_url}\")")
|
||||
local _doh_host=$(echo $_doh_host_port | awk -F ':' '{print $1}')
|
||||
local _is_ip=$(lua_api "is_ip(\"${_doh_host}\")")
|
||||
local _doh_port=$(echo $_doh_host_port | awk -F ':' '{print $2}')
|
||||
[ -z "${_doh_port}" ] && _doh_port=443
|
||||
local _doh_bootstrap=$(echo $remote_dns_doh | cut -d ',' -sf 2-)
|
||||
[ "${_is_ip}" = "true" ] && _doh_bootstrap=${_doh_host}
|
||||
[ -n "${_doh_bootstrap}" ] && REMOTE_DNS=${_doh_bootstrap}:${_doh_port}
|
||||
unset _doh_url _doh_host_port _doh_host _is_ip _doh_port _doh_bootstrap
|
||||
local _doh_url _doh_host _doh_port _doh_bootstrap
|
||||
parse_doh "$remote_dns_doh" _doh_url _doh_host _doh_port _doh_bootstrap
|
||||
[ -n "${_doh_bootstrap}" ] && REMOTE_DNS="${_doh_bootstrap}#${_doh_port}"
|
||||
;;
|
||||
esac
|
||||
_args="${_args} dns_socks_address=127.0.0.1 dns_socks_port=${tcp_node_socks_port}"
|
||||
@@ -1473,16 +1479,9 @@ start_dns() {
|
||||
_args="${_args} remote_dns_doh=${remote_dns_doh}"
|
||||
echolog " - Xray DNS(${TUN_DNS}) -> (${remote_dns_doh})(A/AAAA) + tcp://${REMOTE_DNS}"
|
||||
|
||||
local _doh_url=$(echo $remote_dns_doh | awk -F ',' '{print $1}')
|
||||
local _doh_host_port=$(lua_api "get_domain_from_url(\"${_doh_url}\")")
|
||||
local _doh_host=$(echo $_doh_host_port | awk -F ':' '{print $1}')
|
||||
local _is_ip=$(lua_api "is_ip(\"${_doh_host}\")")
|
||||
local _doh_port=$(echo $_doh_host_port | awk -F ':' '{print $2}')
|
||||
[ -z "${_doh_port}" ] && _doh_port=443
|
||||
local _doh_bootstrap=$(echo $remote_dns_doh | cut -d ',' -sf 2-)
|
||||
[ "${_is_ip}" = "true" ] && _doh_bootstrap=${_doh_host}
|
||||
[ -n "${_doh_bootstrap}" ] && REMOTE_DNS=${REMOTE_DNS},${_doh_bootstrap}:${_doh_port}
|
||||
unset _doh_url _doh_host_port _doh_host _is_ip _doh_port _doh_bootstrap
|
||||
local _doh_url _doh_host _doh_port _doh_bootstrap
|
||||
parse_doh "$remote_dns_doh" _doh_url _doh_host _doh_port _doh_bootstrap
|
||||
[ -n "${_doh_bootstrap}" ] && REMOTE_DNS="${REMOTE_DNS},${_doh_bootstrap}#${_doh_port}"
|
||||
else
|
||||
echolog " - Xray DNS(${TUN_DNS}) -> tcp://${REMOTE_DNS}"
|
||||
fi
|
||||
|
@@ -1011,6 +1011,7 @@ add_firewall_rule() {
|
||||
if [ "$TCP_NODE" != "nil" ]; then
|
||||
_proxy_tcp_access() {
|
||||
[ -n "${2}" ] || return 0
|
||||
if echo "${2}" | grep -q -v ':'; then
|
||||
ipset -q test $IPSET_LANLIST ${2}
|
||||
[ $? -eq 0 ] && {
|
||||
echolog " - 上游 DNS 服务器 ${2} 已在直接访问的列表中,不强制向 TCP 代理转发对该服务器 TCP/${3} 端口的访问"
|
||||
@@ -1023,6 +1024,16 @@ add_firewall_rule() {
|
||||
$ipt_m -I PSW $(comment "本机") -p tcp -i lo -d ${2} --dport ${3} $(REDIRECT $TCP_REDIR_PORT TPROXY)
|
||||
fi
|
||||
echolog " - [$?]将上游 DNS 服务器 ${2}:${3} 加入到路由器自身代理的 TCP 转发链"
|
||||
else
|
||||
ipset -q test $IPSET_LANLIST6 ${2}
|
||||
[ $? -eq 0 ] && {
|
||||
echolog " - 上游 DNS 服务器 ${2} 已在直接访问的列表中,不强制向 TCP 代理转发对该服务器 TCP/${3} 端口的访问"
|
||||
return 0
|
||||
}
|
||||
$ip6t_m -I PSW_OUTPUT -p tcp -d ${2} --dport ${3} -j PSW_RULE
|
||||
$ip6t_m -I PSW $(comment "本机") -p tcp -i lo -d ${2} --dport ${3} $(REDIRECT $TCP_REDIR_PORT TPROXY)
|
||||
echolog " - [$?]将上游 DNS 服务器 [${2}]:${3} 加入到路由器自身代理的 TCP 转发链,请确保您的节点支持IPv6,并开启IPv6透明代理!"
|
||||
fi
|
||||
}
|
||||
[ "$use_tcp_node_resolve_dns" == 1 ] && hosts_foreach REMOTE_DNS _proxy_tcp_access 53
|
||||
|
||||
@@ -1087,6 +1098,7 @@ add_firewall_rule() {
|
||||
if [ "$UDP_NODE" != "nil" -o "$TCP_UDP" = "1" ]; then
|
||||
_proxy_udp_access() {
|
||||
[ -n "${2}" ] || return 0
|
||||
if echo "${2}" | grep -q -v ':'; then
|
||||
ipset -q test $IPSET_LANLIST ${2}
|
||||
[ $? == 0 ] && {
|
||||
echolog " - 上游 DNS 服务器 ${2} 已在直接访问的列表中,不强制向 UDP 代理转发对该服务器 UDP/${3} 端口的访问"
|
||||
@@ -1095,6 +1107,16 @@ add_firewall_rule() {
|
||||
$ipt_m -I PSW_OUTPUT -p udp -d ${2} --dport ${3} -j PSW_RULE
|
||||
$ipt_m -I PSW $(comment "本机") -p udp -i lo -d ${2} --dport ${3} $(REDIRECT $UDP_REDIR_PORT TPROXY)
|
||||
echolog " - [$?]将上游 DNS 服务器 ${2}:${3} 加入到路由器自身代理的 UDP 转发链"
|
||||
else
|
||||
ipset -q test $IPSET_LANLIST6 ${2}
|
||||
[ $? == 0 ] && {
|
||||
echolog " - 上游 DNS 服务器 ${2} 已在直接访问的列表中,不强制向 UDP 代理转发对该服务器 UDP/${3} 端口的访问"
|
||||
return 0
|
||||
}
|
||||
$ip6t_m -I PSW_OUTPUT -p udp -d ${2} --dport ${3} -j PSW_RULE
|
||||
$ip6t_m -I PSW $(comment "本机") -p udp -i lo -d ${2} --dport ${3} $(REDIRECT $UDP_REDIR_PORT TPROXY)
|
||||
echolog " - [$?]将上游 DNS 服务器 [${2}]:${3} 加入到路由器自身代理的 UDP 转发链,请确保您的节点支持IPv6,并开启IPv6透明代理!"
|
||||
fi
|
||||
}
|
||||
[ "$use_udp_node_resolve_dns" == 1 ] && hosts_foreach REMOTE_DNS _proxy_udp_access 53
|
||||
|
||||
|
@@ -1088,6 +1088,7 @@ add_firewall_rule() {
|
||||
if [ "$TCP_NODE" != "nil" ]; then
|
||||
_proxy_tcp_access() {
|
||||
[ -n "${2}" ] || return 0
|
||||
if echo "${2}" | grep -q -v ':'; then
|
||||
nft "get element $NFTABLE_NAME $NFTSET_LANLIST {${2}}" &>/dev/null
|
||||
[ $? -eq 0 ] && {
|
||||
echolog " - 上游 DNS 服务器 ${2} 已在直接访问的列表中,不强制向 TCP 代理转发对该服务器 TCP/${3} 端口的访问"
|
||||
@@ -1100,6 +1101,16 @@ add_firewall_rule() {
|
||||
nft insert rule $NFTABLE_NAME PSW_MANGLE ip protocol tcp iif lo tcp dport ${3} ip daddr ${2} $(REDIRECT $TCP_REDIR_PORT TPROXY4) comment \"本机\"
|
||||
fi
|
||||
echolog " - [$?]将上游 DNS 服务器 ${2}:${3} 加入到路由器自身代理的 TCP 转发链"
|
||||
else
|
||||
nft "get element $NFTABLE_NAME $NFTSET_LANLIST6 {${2}}" &>/dev/null
|
||||
[ $? -eq 0 ] && {
|
||||
echolog " - 上游 DNS 服务器 ${2} 已在直接访问的列表中,不强制向 TCP 代理转发对该服务器 TCP/${3} 端口的访问"
|
||||
return 0
|
||||
}
|
||||
nft "insert rule $NFTABLE_NAME PSW_OUTPUT_MANGLE_V6 meta l4proto tcp ip6 daddr ${2} tcp dport ${3} counter jump PSW_RULE"
|
||||
nft "insert rule $NFTABLE_NAME PSW_MANGLE_V6 meta l4proto tcp iif lo tcp dport ${3} ip6 daddr ${2} $(REDIRECT $TCP_REDIR_PORT TPROXY6) comment \"本机\""
|
||||
echolog " - [$?]将上游 DNS 服务器 [${2}]:${3} 加入到路由器自身代理的 TCP 转发链,请确保您的节点支持IPv6,并开启IPv6透明代理!"
|
||||
fi
|
||||
}
|
||||
[ "$use_tcp_node_resolve_dns" == 1 ] && hosts_foreach REMOTE_DNS _proxy_tcp_access 53
|
||||
|
||||
@@ -1163,6 +1174,7 @@ add_firewall_rule() {
|
||||
if [ "$UDP_NODE" != "nil" -o "$TCP_UDP" = "1" ]; then
|
||||
_proxy_udp_access() {
|
||||
[ -n "${2}" ] || return 0
|
||||
if echo "${2}" | grep -q -v ':'; then
|
||||
nft "get element $NFTABLE_NAME $NFTSET_LANLIST {${2}}" &>/dev/null
|
||||
[ $? == 0 ] && {
|
||||
echolog " - 上游 DNS 服务器 ${2} 已在直接访问的列表中,不强制向 UDP 代理转发对该服务器 UDP/${3} 端口的访问"
|
||||
@@ -1171,6 +1183,16 @@ add_firewall_rule() {
|
||||
nft "insert rule $NFTABLE_NAME PSW_OUTPUT_MANGLE ip protocol udp ip daddr ${2} udp dport ${3} counter jump PSW_RULE"
|
||||
nft "insert rule $NFTABLE_NAME PSW_MANGLE ip protocol udp iif lo ip daddr ${2} $(REDIRECT $UDP_REDIR_PORT TPROXY4) comment \"本机\""
|
||||
echolog " - [$?]将上游 DNS 服务器 ${2}:${3} 加入到路由器自身代理的 UDP 转发链"
|
||||
else
|
||||
nft "get element $NFTABLE_NAME $NFTSET_LANLIST6 {${2}}" &>/dev/null
|
||||
[ $? == 0 ] && {
|
||||
echolog " - 上游 DNS 服务器 ${2} 已在直接访问的列表中,不强制向 UDP 代理转发对该服务器 UDP/${3} 端口的访问"
|
||||
return 0
|
||||
}
|
||||
nft "insert rule $NFTABLE_NAME PSW_OUTPUT_MANGLE_V6 meta l4proto udp ip6 daddr ${2} udp dport ${3} counter jump PSW_RULE"
|
||||
nft "insert rule $NFTABLE_NAME PSW_MANGLE_V6 meta l4proto tcp iif lo ip6 daddr ${2} $(REDIRECT $UDP_REDIR_PORT TPROXY6) comment \"本机\""
|
||||
echolog " - [$?]将上游 DNS 服务器 [${2}]:${3} 加入到路由器自身代理的 UDP 转发链,请确保您的节点支持IPv6,并开启IPv6透明代理!"
|
||||
fi
|
||||
}
|
||||
[ "$use_udp_node_resolve_dns" == 1 ] && hosts_foreach REMOTE_DNS _proxy_udp_access 53
|
||||
[ -n "${LOCALHOST_UDP_PROXY_MODE}" ] && {
|
||||
|
@@ -39,9 +39,9 @@
|
||||
<PackageVersion Include="securifybv.ShellLink" Version="0.1.0" />
|
||||
<PackageVersion Include="shaderc.net" Version="0.1.0" />
|
||||
<PackageVersion Include="SharpZipLib" Version="1.4.2" />
|
||||
<PackageVersion Include="Silk.NET.Vulkan" Version="2.16.0" />
|
||||
<PackageVersion Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.16.0" />
|
||||
<PackageVersion Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.16.0" />
|
||||
<PackageVersion Include="Silk.NET.Vulkan" Version="2.21.0" />
|
||||
<PackageVersion Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.21.0" />
|
||||
<PackageVersion Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.21.0" />
|
||||
<PackageVersion Include="SkiaSharp" Version="2.88.7" />
|
||||
<PackageVersion Include="SkiaSharp.NativeAssets.Linux" Version="2.88.7" />
|
||||
<PackageVersion Include="SPB" Version="0.0.4-build32" />
|
||||
|
@@ -1,13 +1,33 @@
|
||||
using Ryujinx.Common.Utilities;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Common.GraphicsDriver
|
||||
{
|
||||
public static class DriverUtilities
|
||||
{
|
||||
private static void AddMesaFlags(string envVar, string newFlags)
|
||||
{
|
||||
string existingFlags = Environment.GetEnvironmentVariable(envVar);
|
||||
|
||||
string flags = existingFlags == null ? newFlags : $"{existingFlags},{newFlags}";
|
||||
|
||||
OsUtils.SetEnvironmentVariableNoCaching(envVar, flags);
|
||||
}
|
||||
|
||||
public static void InitDriverConfig(bool oglThreading)
|
||||
{
|
||||
if (OperatingSystem.IsLinux())
|
||||
{
|
||||
AddMesaFlags("RADV_DEBUG", "nodcc");
|
||||
}
|
||||
|
||||
ToggleOGLThreading(oglThreading);
|
||||
}
|
||||
|
||||
public static void ToggleOGLThreading(bool enabled)
|
||||
{
|
||||
Environment.SetEnvironmentVariable("mesa_glthread", enabled.ToString().ToLower());
|
||||
Environment.SetEnvironmentVariable("__GL_THREADED_OPTIMIZATIONS", enabled ? "1" : "0");
|
||||
OsUtils.SetEnvironmentVariableNoCaching("mesa_glthread", enabled.ToString().ToLower());
|
||||
OsUtils.SetEnvironmentVariableNoCaching("__GL_THREADED_OPTIMIZATIONS", enabled ? "1" : "0");
|
||||
|
||||
try
|
||||
{
|
||||
|
24
ryujinx/src/Ryujinx.Common/Utilities/OsUtils.cs
Normal file
24
ryujinx/src/Ryujinx.Common/Utilities/OsUtils.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Common.Utilities
|
||||
{
|
||||
public partial class OsUtils
|
||||
{
|
||||
[LibraryImport("libc", SetLastError = true)]
|
||||
private static partial int setenv([MarshalAs(UnmanagedType.LPStr)] string name, [MarshalAs(UnmanagedType.LPStr)] string value, int overwrite);
|
||||
|
||||
public static void SetEnvironmentVariableNoCaching(string key, string value)
|
||||
{
|
||||
// Set the value in the cached environment variables, too.
|
||||
Environment.SetEnvironmentVariable(key, value);
|
||||
|
||||
if (!OperatingSystem.IsWindows())
|
||||
{
|
||||
int res = setenv(key, value, 1);
|
||||
Debug.Assert(res != -1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -415,7 +415,13 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||
#pragma warning disable CS0649 // Field is never assigned to
|
||||
public int Width;
|
||||
public int Height;
|
||||
public int Depth;
|
||||
public ushort Depth;
|
||||
public ushort Flags;
|
||||
|
||||
public readonly bool UnpackIsLayered()
|
||||
{
|
||||
return (Flags & 1) == 0;
|
||||
}
|
||||
#pragma warning restore CS0649
|
||||
}
|
||||
|
||||
|
@@ -468,13 +468,11 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
int gobBlocksInY = dsState.MemoryLayout.UnpackGobBlocksInY();
|
||||
int gobBlocksInZ = dsState.MemoryLayout.UnpackGobBlocksInZ();
|
||||
|
||||
layered &= size.UnpackIsLayered();
|
||||
|
||||
Target target;
|
||||
|
||||
if (dsState.MemoryLayout.UnpackIsTarget3D())
|
||||
{
|
||||
target = Target.Texture3D;
|
||||
}
|
||||
else if ((samplesInX | samplesInY) != 1)
|
||||
if ((samplesInX | samplesInY) != 1)
|
||||
{
|
||||
target = size.Depth > 1 && layered
|
||||
? Target.Texture2DMultisampleArray
|
||||
|
@@ -32,10 +32,12 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
CommandBuffer
|
||||
}
|
||||
|
||||
private bool _feedbackLoopActive;
|
||||
private PipelineStageFlags _incoherentBufferWriteStages;
|
||||
private PipelineStageFlags _incoherentTextureWriteStages;
|
||||
private PipelineStageFlags _extraStages;
|
||||
private IncoherentBarrierType _queuedIncoherentBarrier;
|
||||
private bool _queuedFeedbackLoopBarrier;
|
||||
|
||||
public BarrierBatch(VulkanRenderer gd)
|
||||
{
|
||||
@@ -53,17 +55,6 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
stages |= PipelineStageFlags.TransformFeedbackBitExt;
|
||||
}
|
||||
|
||||
if (!gd.IsTBDR)
|
||||
{
|
||||
// Desktop GPUs can transform image barriers into memory barriers.
|
||||
|
||||
access |= AccessFlags.DepthStencilAttachmentWriteBit | AccessFlags.ColorAttachmentWriteBit;
|
||||
access |= AccessFlags.DepthStencilAttachmentReadBit | AccessFlags.ColorAttachmentReadBit;
|
||||
|
||||
stages |= PipelineStageFlags.EarlyFragmentTestsBit | PipelineStageFlags.LateFragmentTestsBit;
|
||||
stages |= PipelineStageFlags.ColorAttachmentOutputBit;
|
||||
}
|
||||
|
||||
return (access, stages);
|
||||
}
|
||||
|
||||
@@ -178,16 +169,34 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
}
|
||||
|
||||
_queuedIncoherentBarrier = IncoherentBarrierType.None;
|
||||
_queuedFeedbackLoopBarrier = false;
|
||||
}
|
||||
else if (_feedbackLoopActive && _queuedFeedbackLoopBarrier)
|
||||
{
|
||||
// Feedback loop barrier.
|
||||
|
||||
MemoryBarrier barrier = new MemoryBarrier()
|
||||
{
|
||||
SType = StructureType.MemoryBarrier,
|
||||
SrcAccessMask = AccessFlags.ShaderWriteBit,
|
||||
DstAccessMask = AccessFlags.ShaderReadBit
|
||||
};
|
||||
|
||||
QueueBarrier(barrier, PipelineStageFlags.FragmentShaderBit, PipelineStageFlags.AllGraphicsBit);
|
||||
|
||||
_queuedFeedbackLoopBarrier = false;
|
||||
}
|
||||
|
||||
_feedbackLoopActive = false;
|
||||
}
|
||||
}
|
||||
|
||||
public unsafe void Flush(CommandBufferScoped cbs, bool inRenderPass, RenderPassHolder rpHolder, Action endRenderPass)
|
||||
{
|
||||
Flush(cbs, null, inRenderPass, rpHolder, endRenderPass);
|
||||
Flush(cbs, null, false, inRenderPass, rpHolder, endRenderPass);
|
||||
}
|
||||
|
||||
public unsafe void Flush(CommandBufferScoped cbs, ShaderCollection program, bool inRenderPass, RenderPassHolder rpHolder, Action endRenderPass)
|
||||
public unsafe void Flush(CommandBufferScoped cbs, ShaderCollection program, bool feedbackLoopActive, bool inRenderPass, RenderPassHolder rpHolder, Action endRenderPass)
|
||||
{
|
||||
if (program != null)
|
||||
{
|
||||
@@ -195,6 +204,8 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
_incoherentTextureWriteStages |= program.IncoherentTextureWriteStages;
|
||||
}
|
||||
|
||||
_feedbackLoopActive |= feedbackLoopActive;
|
||||
|
||||
FlushMemoryBarrier(program, inRenderPass);
|
||||
|
||||
if (!inRenderPass && rpHolder != null)
|
||||
@@ -406,6 +417,8 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
_queuedIncoherentBarrier = type;
|
||||
}
|
||||
|
||||
_queuedFeedbackLoopBarrier = true;
|
||||
}
|
||||
|
||||
public void QueueTextureBarrier()
|
||||
|
@@ -122,7 +122,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
Range = (uint)size,
|
||||
};
|
||||
|
||||
_gd.Api.CreateBufferView(_device, bufferViewCreateInfo, null, out var bufferView).ThrowOnError();
|
||||
_gd.Api.CreateBufferView(_device, in bufferViewCreateInfo, null, out var bufferView).ThrowOnError();
|
||||
|
||||
return new Auto<DisposableBufferView>(new DisposableBufferView(_gd.Api, _device, bufferView), this, _waitable, _buffer);
|
||||
}
|
||||
@@ -153,7 +153,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
PipelineStageFlags.AllCommandsBit,
|
||||
DependencyFlags.DeviceGroupBit,
|
||||
1,
|
||||
memoryBarrier,
|
||||
in memoryBarrier,
|
||||
0,
|
||||
null,
|
||||
0,
|
||||
@@ -770,7 +770,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
0,
|
||||
null,
|
||||
1,
|
||||
memoryBarrier,
|
||||
in memoryBarrier,
|
||||
0,
|
||||
null);
|
||||
}
|
||||
|
@@ -221,7 +221,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
PBufferBinds = &bufferBind
|
||||
};
|
||||
|
||||
gd.Api.QueueBindSparse(gd.Queue, 1, bindSparseInfo, default).ThrowOnError();
|
||||
gd.Api.QueueBindSparse(gd.Queue, 1, in bindSparseInfo, default).ThrowOnError();
|
||||
}
|
||||
|
||||
var holder = new BufferHolder(gd, _device, buffer, (int)size, storageAllocations);
|
||||
|
@@ -25,7 +25,10 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
var buffer = _buffer.Get(cbs, _offset, _size, true).Value;
|
||||
|
||||
gd.TransformFeedbackApi.CmdBindTransformFeedbackBuffers(cbs.CommandBuffer, binding, 1, buffer, (ulong)_offset, (ulong)_size);
|
||||
ulong offset = (ulong)_offset;
|
||||
ulong size = (ulong)_size;
|
||||
|
||||
gd.TransformFeedbackApi.CmdBindTransformFeedbackBuffers(cbs.CommandBuffer, binding, 1, in buffer, in offset, in size);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -45,7 +45,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
Level = CommandBufferLevel.Primary,
|
||||
};
|
||||
|
||||
api.AllocateCommandBuffers(device, allocateInfo, out CommandBuffer);
|
||||
api.AllocateCommandBuffers(device, in allocateInfo, out CommandBuffer);
|
||||
|
||||
Dependants = new List<IAuto>();
|
||||
Waitables = new List<MultiFenceHolder>();
|
||||
@@ -83,7 +83,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
CommandPoolCreateFlags.ResetCommandBufferBit,
|
||||
};
|
||||
|
||||
api.CreateCommandPool(device, commandPoolCreateInfo, null, out _pool).ThrowOnError();
|
||||
api.CreateCommandPool(device, in commandPoolCreateInfo, null, out _pool).ThrowOnError();
|
||||
|
||||
// We need at least 2 command buffers to get texture data in some cases.
|
||||
_totalCommandBuffers = isLight ? 2 : MaxCommandBuffers;
|
||||
@@ -253,7 +253,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
SType = StructureType.CommandBufferBeginInfo,
|
||||
};
|
||||
|
||||
_api.BeginCommandBuffer(entry.CommandBuffer, commandBufferBeginInfo).ThrowOnError();
|
||||
_api.BeginCommandBuffer(entry.CommandBuffer, in commandBufferBeginInfo).ThrowOnError();
|
||||
|
||||
return new CommandBufferScoped(this, entry.CommandBuffer, cursor);
|
||||
}
|
||||
@@ -311,7 +311,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
lock (_queueLock)
|
||||
{
|
||||
_api.QueueSubmit(_queue, 1, sInfo, entry.Fence.GetUnsafe()).ThrowOnError();
|
||||
_api.QueueSubmit(_queue, 1, in sInfo, entry.Fence.GetUnsafe()).ThrowOnError();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -43,7 +43,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
PBufferInfo = &bufferInfo,
|
||||
};
|
||||
|
||||
_holder.Api.UpdateDescriptorSets(_holder.Device, 1, writeDescriptorSet, 0, null);
|
||||
_holder.Api.UpdateDescriptorSets(_holder.Device, 1, in writeDescriptorSet, 0, null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
PBufferInfo = pBufferInfo,
|
||||
};
|
||||
|
||||
_holder.Api.UpdateDescriptorSets(_holder.Device, 1, writeDescriptorSet, 0, null);
|
||||
_holder.Api.UpdateDescriptorSets(_holder.Device, 1, in writeDescriptorSet, 0, null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
PImageInfo = &imageInfo,
|
||||
};
|
||||
|
||||
_holder.Api.UpdateDescriptorSets(_holder.Device, 1, writeDescriptorSet, 0, null);
|
||||
_holder.Api.UpdateDescriptorSets(_holder.Device, 1, in writeDescriptorSet, 0, null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,7 +107,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
PImageInfo = pImageInfo,
|
||||
};
|
||||
|
||||
_holder.Api.UpdateDescriptorSets(_holder.Device, 1, writeDescriptorSet, 0, null);
|
||||
_holder.Api.UpdateDescriptorSets(_holder.Device, 1, in writeDescriptorSet, 0, null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,7 +144,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
PImageInfo = pImageInfo,
|
||||
};
|
||||
|
||||
_holder.Api.UpdateDescriptorSets(_holder.Device, 1, writeDescriptorSet, 0, null);
|
||||
_holder.Api.UpdateDescriptorSets(_holder.Device, 1, in writeDescriptorSet, 0, null);
|
||||
|
||||
i += count - 1;
|
||||
}
|
||||
@@ -166,7 +166,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
PTexelBufferView = &texelBufferView,
|
||||
};
|
||||
|
||||
_holder.Api.UpdateDescriptorSets(_holder.Device, 1, writeDescriptorSet, 0, null);
|
||||
_holder.Api.UpdateDescriptorSets(_holder.Device, 1, in writeDescriptorSet, 0, null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -200,7 +200,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
PTexelBufferView = pTexelBufferView + i,
|
||||
};
|
||||
|
||||
_holder.Api.UpdateDescriptorSets(_holder.Device, 1, writeDescriptorSet, 0, null);
|
||||
_holder.Api.UpdateDescriptorSets(_holder.Device, 1, in writeDescriptorSet, 0, null);
|
||||
}
|
||||
|
||||
i += count;
|
||||
|
@@ -40,7 +40,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
PPoolSizes = pPoolsSize,
|
||||
};
|
||||
|
||||
Api.CreateDescriptorPool(device, descriptorPoolCreateInfo, null, out _pool).ThrowOnError();
|
||||
Api.CreateDescriptorPool(device, in descriptorPoolCreateInfo, null, out _pool).ThrowOnError();
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -4,6 +4,7 @@ using Ryujinx.Graphics.Shader;
|
||||
using Silk.NET.Vulkan;
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using CompareOp = Ryujinx.Graphics.GAL.CompareOp;
|
||||
@@ -42,15 +43,15 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
private record struct TextureRef
|
||||
{
|
||||
public ShaderStage Stage;
|
||||
public TextureStorage Storage;
|
||||
public Auto<DisposableImageView> View;
|
||||
public TextureView View;
|
||||
public Auto<DisposableImageView> ImageView;
|
||||
public Auto<DisposableSampler> Sampler;
|
||||
|
||||
public TextureRef(ShaderStage stage, TextureStorage storage, Auto<DisposableImageView> view, Auto<DisposableSampler> sampler)
|
||||
public TextureRef(ShaderStage stage, TextureView view, Auto<DisposableImageView> imageView, Auto<DisposableSampler> sampler)
|
||||
{
|
||||
Stage = stage;
|
||||
Storage = storage;
|
||||
View = view;
|
||||
ImageView = imageView;
|
||||
Sampler = sampler;
|
||||
}
|
||||
}
|
||||
@@ -58,14 +59,14 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
private record struct ImageRef
|
||||
{
|
||||
public ShaderStage Stage;
|
||||
public TextureStorage Storage;
|
||||
public Auto<DisposableImageView> View;
|
||||
public TextureView View;
|
||||
public Auto<DisposableImageView> ImageView;
|
||||
|
||||
public ImageRef(ShaderStage stage, TextureStorage storage, Auto<DisposableImageView> view)
|
||||
public ImageRef(ShaderStage stage, TextureView view, Auto<DisposableImageView> imageView)
|
||||
{
|
||||
Stage = stage;
|
||||
Storage = storage;
|
||||
View = view;
|
||||
ImageView = imageView;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,6 +125,8 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
private readonly TextureView _dummyTexture;
|
||||
private readonly SamplerHolder _dummySampler;
|
||||
|
||||
public List<TextureView> FeedbackLoopHazards { get; private set; }
|
||||
|
||||
public DescriptorSetUpdater(VulkanRenderer gd, Device device)
|
||||
{
|
||||
_gd = gd;
|
||||
@@ -209,10 +212,15 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
_templateUpdater = new();
|
||||
}
|
||||
|
||||
public void Initialize()
|
||||
public void Initialize(bool isMainPipeline)
|
||||
{
|
||||
MemoryOwner<byte> dummyTextureData = MemoryOwner<byte>.RentCleared(4);
|
||||
_dummyTexture.SetData(dummyTextureData);
|
||||
|
||||
if (isMainPipeline)
|
||||
{
|
||||
FeedbackLoopHazards = new();
|
||||
}
|
||||
}
|
||||
|
||||
private static bool BindingOverlaps(ref DescriptorBufferInfo info, int bindingOffset, int offset, int size)
|
||||
@@ -275,6 +283,18 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
public void InsertBindingBarriers(CommandBufferScoped cbs)
|
||||
{
|
||||
if ((FeedbackLoopHazards?.Count ?? 0) > 0)
|
||||
{
|
||||
// Clear existing hazards - they will be rebuilt.
|
||||
|
||||
foreach (TextureView hazard in FeedbackLoopHazards)
|
||||
{
|
||||
hazard.DecrementHazardUses();
|
||||
}
|
||||
|
||||
FeedbackLoopHazards.Clear();
|
||||
}
|
||||
|
||||
foreach (ResourceBindingSegment segment in _program.BindingSegments[PipelineBase.TextureSetIndex])
|
||||
{
|
||||
if (segment.Type == ResourceType.TextureAndSampler)
|
||||
@@ -284,7 +304,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
for (int i = 0; i < segment.Count; i++)
|
||||
{
|
||||
ref var texture = ref _textureRefs[segment.Binding + i];
|
||||
texture.Storage?.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, texture.Stage.ConvertToPipelineStageFlags());
|
||||
texture.View?.PrepareForUsage(cbs, texture.Stage.ConvertToPipelineStageFlags(), FeedbackLoopHazards);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -305,7 +325,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
for (int i = 0; i < segment.Count; i++)
|
||||
{
|
||||
ref var image = ref _imageRefs[segment.Binding + i];
|
||||
image.Storage?.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, image.Stage.ConvertToPipelineStageFlags());
|
||||
image.View?.PrepareForUsage(cbs, image.Stage.ConvertToPipelineStageFlags(), FeedbackLoopHazards);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -385,9 +405,12 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
}
|
||||
else if (image is TextureView view)
|
||||
{
|
||||
view.Storage.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, stage.ConvertToPipelineStageFlags());
|
||||
ref ImageRef iRef = ref _imageRefs[binding];
|
||||
|
||||
_imageRefs[binding] = new(stage, view.Storage, view.GetView(imageFormat).GetIdentityImageView());
|
||||
iRef.View?.ClearUsage(FeedbackLoopHazards);
|
||||
view?.PrepareForUsage(cbs, stage.ConvertToPipelineStageFlags(), FeedbackLoopHazards);
|
||||
|
||||
iRef = new(stage, view, view.GetView(imageFormat).GetIdentityImageView());
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -486,9 +509,12 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
}
|
||||
else if (texture is TextureView view)
|
||||
{
|
||||
view.Storage.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, stage.ConvertToPipelineStageFlags());
|
||||
ref TextureRef iRef = ref _textureRefs[binding];
|
||||
|
||||
_textureRefs[binding] = new(stage, view.Storage, view.GetImageView(), ((SamplerHolder)sampler)?.GetSampler());
|
||||
iRef.View?.ClearUsage(FeedbackLoopHazards);
|
||||
view?.PrepareForUsage(cbs, stage.ConvertToPipelineStageFlags(), FeedbackLoopHazards);
|
||||
|
||||
iRef = new(stage, view, view.GetImageView(), ((SamplerHolder)sampler)?.GetSampler());
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -510,7 +536,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
view.Storage.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, stage.ConvertToPipelineStageFlags());
|
||||
|
||||
_textureRefs[binding] = new(stage, view.Storage, view.GetIdentityImageView(), ((SamplerHolder)sampler)?.GetSampler());
|
||||
_textureRefs[binding] = new(stage, view, view.GetIdentityImageView(), ((SamplerHolder)sampler)?.GetSampler());
|
||||
|
||||
SignalDirty(DirtyFlags.Texture);
|
||||
}
|
||||
@@ -836,7 +862,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
ref var texture = ref textures[i];
|
||||
ref var refs = ref _textureRefs[binding + i];
|
||||
|
||||
texture.ImageView = refs.View?.Get(cbs).Value ?? default;
|
||||
texture.ImageView = refs.ImageView?.Get(cbs).Value ?? default;
|
||||
texture.Sampler = refs.Sampler?.Get(cbs).Value ?? default;
|
||||
|
||||
if (texture.ImageView.Handle == 0)
|
||||
@@ -886,7 +912,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
images[i].ImageView = _imageRefs[binding + i].View?.Get(cbs).Value ?? default;
|
||||
images[i].ImageView = _imageRefs[binding + i].ImageView?.Get(cbs).Value ?? default;
|
||||
}
|
||||
|
||||
tu.Push<DescriptorImageInfo>(images[..count]);
|
||||
@@ -957,7 +983,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
ref var texture = ref textures[i];
|
||||
ref var refs = ref _textureRefs[binding + i];
|
||||
|
||||
texture.ImageView = refs.View?.Get(cbs).Value ?? default;
|
||||
texture.ImageView = refs.ImageView?.Get(cbs).Value ?? default;
|
||||
texture.Sampler = refs.Sampler?.Get(cbs).Value ?? default;
|
||||
|
||||
if (texture.ImageView.Handle == 0)
|
||||
|
12
ryujinx/src/Ryujinx.Graphics.Vulkan/FeedbackLoopAspects.cs
Normal file
12
ryujinx/src/Ryujinx.Graphics.Vulkan/FeedbackLoopAspects.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
[Flags]
|
||||
internal enum FeedbackLoopAspects
|
||||
{
|
||||
None = 0,
|
||||
Color = 1 << 0,
|
||||
Depth = 1 << 1,
|
||||
}
|
||||
}
|
@@ -250,7 +250,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
Layers = Layers,
|
||||
};
|
||||
|
||||
api.CreateFramebuffer(_device, framebufferCreateInfo, null, out var framebuffer).ThrowOnError();
|
||||
api.CreateFramebuffer(_device, in framebufferCreateInfo, null, out var framebuffer).ThrowOnError();
|
||||
return new Auto<DisposableFramebuffer>(new DisposableFramebuffer(api, _device, framebuffer), null, _attachments);
|
||||
}
|
||||
|
||||
@@ -302,6 +302,27 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
_depthStencil?.Storage?.AddStoreOpUsage(true);
|
||||
}
|
||||
|
||||
public void ClearBindings()
|
||||
{
|
||||
_depthStencil?.Storage.ClearBindings();
|
||||
|
||||
for (int i = 0; i < _colorsCanonical.Length; i++)
|
||||
{
|
||||
_colorsCanonical[i]?.Storage.ClearBindings();
|
||||
}
|
||||
}
|
||||
|
||||
public void AddBindings()
|
||||
{
|
||||
_depthStencil?.Storage.AddBinding(_depthStencil);
|
||||
|
||||
for (int i = 0; i < _colorsCanonical.Length; i++)
|
||||
{
|
||||
TextureView color = _colorsCanonical[i];
|
||||
color?.Storage.AddBinding(color);
|
||||
}
|
||||
}
|
||||
|
||||
public (RenderPassHolder rpHolder, Auto<DisposableFramebuffer> framebuffer) GetPassAndFramebuffer(
|
||||
VulkanRenderer gd,
|
||||
Device device,
|
||||
|
@@ -46,6 +46,8 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
public readonly bool SupportsViewportArray2;
|
||||
public readonly bool SupportsHostImportedMemory;
|
||||
public readonly bool SupportsDepthClipControl;
|
||||
public readonly bool SupportsAttachmentFeedbackLoop;
|
||||
public readonly bool SupportsDynamicAttachmentFeedbackLoop;
|
||||
public readonly uint SubgroupSize;
|
||||
public readonly SampleCountFlags SupportedSampleCounts;
|
||||
public readonly PortabilitySubsetFlags PortabilitySubset;
|
||||
@@ -84,6 +86,8 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
bool supportsViewportArray2,
|
||||
bool supportsHostImportedMemory,
|
||||
bool supportsDepthClipControl,
|
||||
bool supportsAttachmentFeedbackLoop,
|
||||
bool supportsDynamicAttachmentFeedbackLoop,
|
||||
uint subgroupSize,
|
||||
SampleCountFlags supportedSampleCounts,
|
||||
PortabilitySubsetFlags portabilitySubset,
|
||||
@@ -121,6 +125,8 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
SupportsViewportArray2 = supportsViewportArray2;
|
||||
SupportsHostImportedMemory = supportsHostImportedMemory;
|
||||
SupportsDepthClipControl = supportsDepthClipControl;
|
||||
SupportsAttachmentFeedbackLoop = supportsAttachmentFeedbackLoop;
|
||||
SupportsDynamicAttachmentFeedbackLoop = supportsDynamicAttachmentFeedbackLoop;
|
||||
SubgroupSize = subgroupSize;
|
||||
SupportedSampleCounts = supportedSampleCounts;
|
||||
PortabilitySubset = portabilitySubset;
|
||||
|
@@ -115,7 +115,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
PNext = &importInfo,
|
||||
};
|
||||
|
||||
Result result = _api.AllocateMemory(_device, memoryAllocateInfo, null, out var deviceMemory);
|
||||
Result result = _api.AllocateMemory(_device, in memoryAllocateInfo, null, out var deviceMemory);
|
||||
|
||||
if (result < Result.Success)
|
||||
{
|
||||
|
@@ -220,7 +220,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
MemoryTypeIndex = (uint)MemoryTypeIndex,
|
||||
};
|
||||
|
||||
_api.AllocateMemory(_device, memoryAllocateInfo, null, out var deviceMemory).ThrowOnError();
|
||||
_api.AllocateMemory(_device, in memoryAllocateInfo, null, out var deviceMemory).ThrowOnError();
|
||||
|
||||
IntPtr hostPointer = IntPtr.Zero;
|
||||
|
||||
|
@@ -1,3 +1,4 @@
|
||||
using Silk.NET.Core.Loader;
|
||||
using Silk.NET.Vulkan;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
@@ -8,6 +9,8 @@ namespace Ryujinx.Graphics.Vulkan.MoltenVK
|
||||
[SupportedOSPlatform("macos")]
|
||||
public static partial class MVKInitialization
|
||||
{
|
||||
private const string VulkanLib = "libvulkan.dylib";
|
||||
|
||||
[LibraryImport("libMoltenVK.dylib")]
|
||||
private static partial Result vkGetMoltenVKConfigurationMVK(IntPtr unusedInstance, out MVKConfiguration config, in IntPtr configSize);
|
||||
|
||||
@@ -29,5 +32,20 @@ namespace Ryujinx.Graphics.Vulkan.MoltenVK
|
||||
|
||||
vkSetMoltenVKConfigurationMVK(IntPtr.Zero, config, configSize);
|
||||
}
|
||||
|
||||
private static string[] Resolver(string path)
|
||||
{
|
||||
if (path.EndsWith(VulkanLib))
|
||||
{
|
||||
path = path[..^VulkanLib.Length] + "libMoltenVK.dylib";
|
||||
return [path];
|
||||
}
|
||||
return Array.Empty<string>();
|
||||
}
|
||||
|
||||
public static void InitializeResolver()
|
||||
{
|
||||
((DefaultPathResolver)PathResolver.Default).Resolvers.Insert(0, Resolver);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -2,6 +2,7 @@ using Ryujinx.Graphics.GAL;
|
||||
using Ryujinx.Graphics.Shader;
|
||||
using Silk.NET.Vulkan;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
@@ -33,6 +34,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
public readonly Action EndRenderPassDelegate;
|
||||
|
||||
protected PipelineDynamicState DynamicState;
|
||||
protected bool IsMainPipeline;
|
||||
private PipelineState _newState;
|
||||
private bool _graphicsStateDirty;
|
||||
private bool _computeStateDirty;
|
||||
@@ -85,6 +87,9 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
private bool _tfEnabled;
|
||||
private bool _tfActive;
|
||||
|
||||
private FeedbackLoopAspects _feedbackLoop;
|
||||
private bool _passWritesDepthStencil;
|
||||
|
||||
private readonly PipelineColorBlendAttachmentState[] _storedBlend;
|
||||
public ulong DrawCount { get; private set; }
|
||||
public bool RenderPassActive { get; private set; }
|
||||
@@ -102,7 +107,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
SType = StructureType.PipelineCacheCreateInfo,
|
||||
};
|
||||
|
||||
gd.Api.CreatePipelineCache(device, pipelineCacheCreateInfo, null, out PipelineCache).ThrowOnError();
|
||||
gd.Api.CreatePipelineCache(device, in pipelineCacheCreateInfo, null, out PipelineCache).ThrowOnError();
|
||||
|
||||
_descriptorSetUpdater = new DescriptorSetUpdater(gd, device);
|
||||
_vertexBufferUpdater = new VertexBufferUpdater(gd);
|
||||
@@ -126,7 +131,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
_descriptorSetUpdater.Initialize();
|
||||
_descriptorSetUpdater.Initialize(IsMainPipeline);
|
||||
|
||||
QuadsToTrisPattern = new IndexBufferPattern(Gd, 4, 6, 0, new[] { 0, 1, 2, 0, 2, 3 }, 4, false);
|
||||
TriFanToTrisPattern = new IndexBufferPattern(Gd, 3, 3, 2, new[] { int.MinValue, -1, 0 }, 1, true);
|
||||
@@ -814,6 +819,8 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
_newState.DepthTestEnable = depthTest.TestEnable;
|
||||
_newState.DepthWriteEnable = depthTest.WriteEnable;
|
||||
_newState.DepthCompareOp = depthTest.Func.Convert();
|
||||
|
||||
UpdatePassDepthStencil();
|
||||
SignalStateChange();
|
||||
}
|
||||
|
||||
@@ -1079,6 +1086,8 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
_newState.StencilFrontPassOp = stencilTest.FrontDpPass.Convert();
|
||||
_newState.StencilFrontDepthFailOp = stencilTest.FrontDpFail.Convert();
|
||||
_newState.StencilFrontCompareOp = stencilTest.FrontFunc.Convert();
|
||||
|
||||
UpdatePassDepthStencil();
|
||||
SignalStateChange();
|
||||
}
|
||||
|
||||
@@ -1426,7 +1435,23 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
}
|
||||
}
|
||||
|
||||
if (IsMainPipeline)
|
||||
{
|
||||
FramebufferParams?.ClearBindings();
|
||||
}
|
||||
|
||||
FramebufferParams = new FramebufferParams(Device, colors, depthStencil);
|
||||
|
||||
if (IsMainPipeline)
|
||||
{
|
||||
FramebufferParams.AddBindings();
|
||||
|
||||
_newState.FeedbackLoopAspects = FeedbackLoopAspects.None;
|
||||
_bindingBarriersDirty = true;
|
||||
}
|
||||
|
||||
_passWritesDepthStencil = false;
|
||||
UpdatePassDepthStencil();
|
||||
UpdatePipelineAttachmentFormats();
|
||||
}
|
||||
|
||||
@@ -1493,11 +1518,82 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
}
|
||||
}
|
||||
|
||||
Gd.Barriers.Flush(Cbs, _program, RenderPassActive, _rpHolder, EndRenderPassDelegate);
|
||||
Gd.Barriers.Flush(Cbs, _program, _feedbackLoop != 0, RenderPassActive, _rpHolder, EndRenderPassDelegate);
|
||||
|
||||
_descriptorSetUpdater.UpdateAndBindDescriptorSets(Cbs, PipelineBindPoint.Compute);
|
||||
}
|
||||
|
||||
private bool ChangeFeedbackLoop(FeedbackLoopAspects aspects)
|
||||
{
|
||||
if (_feedbackLoop != aspects)
|
||||
{
|
||||
if (Gd.Capabilities.SupportsDynamicAttachmentFeedbackLoop)
|
||||
{
|
||||
DynamicState.SetFeedbackLoop(aspects);
|
||||
}
|
||||
else
|
||||
{
|
||||
_newState.FeedbackLoopAspects = aspects;
|
||||
}
|
||||
|
||||
_feedbackLoop = aspects;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private bool UpdateFeedbackLoop()
|
||||
{
|
||||
List<TextureView> hazards = _descriptorSetUpdater.FeedbackLoopHazards;
|
||||
|
||||
if ((hazards?.Count ?? 0) > 0)
|
||||
{
|
||||
FeedbackLoopAspects aspects = 0;
|
||||
|
||||
foreach (TextureView view in hazards)
|
||||
{
|
||||
// May need to enforce feedback loop layout here in the future.
|
||||
// Though technically, it should always work with the general layout.
|
||||
|
||||
if (view.Info.Format.IsDepthOrStencil())
|
||||
{
|
||||
if (_passWritesDepthStencil)
|
||||
{
|
||||
// If depth/stencil isn't written in the pass, it doesn't count as a feedback loop.
|
||||
|
||||
aspects |= FeedbackLoopAspects.Depth;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
aspects |= FeedbackLoopAspects.Color;
|
||||
}
|
||||
}
|
||||
|
||||
return ChangeFeedbackLoop(aspects);
|
||||
}
|
||||
else if (_feedbackLoop != 0)
|
||||
{
|
||||
return ChangeFeedbackLoop(FeedbackLoopAspects.None);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void UpdatePassDepthStencil()
|
||||
{
|
||||
if (!RenderPassActive)
|
||||
{
|
||||
_passWritesDepthStencil = false;
|
||||
}
|
||||
|
||||
// Stencil test being enabled doesn't necessarily mean a write, but it's not critical to check.
|
||||
_passWritesDepthStencil |= (_newState.DepthTestEnable && _newState.DepthWriteEnable) || _newState.StencilTestEnable;
|
||||
}
|
||||
|
||||
private bool RecreateGraphicsPipelineIfNeeded()
|
||||
{
|
||||
if (AutoFlush.ShouldFlushDraw(DrawCount))
|
||||
@@ -1505,7 +1601,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
Gd.FlushAllCommands();
|
||||
}
|
||||
|
||||
DynamicState.ReplayIfDirty(Gd.Api, CommandBuffer);
|
||||
DynamicState.ReplayIfDirty(Gd, CommandBuffer);
|
||||
|
||||
if (_needsIndexBufferRebind && _indexBufferPattern == null)
|
||||
{
|
||||
@@ -1539,7 +1635,15 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
_vertexBufferUpdater.Commit(Cbs);
|
||||
}
|
||||
|
||||
if (_graphicsStateDirty || Pbp != PipelineBindPoint.Graphics)
|
||||
if (_bindingBarriersDirty)
|
||||
{
|
||||
// Stale barriers may have been activated by switching program. Emit any that are relevant.
|
||||
_descriptorSetUpdater.InsertBindingBarriers(Cbs);
|
||||
|
||||
_bindingBarriersDirty = false;
|
||||
}
|
||||
|
||||
if (UpdateFeedbackLoop() || _graphicsStateDirty || Pbp != PipelineBindPoint.Graphics)
|
||||
{
|
||||
if (!CreatePipeline(PipelineBindPoint.Graphics))
|
||||
{
|
||||
@@ -1548,17 +1652,9 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
_graphicsStateDirty = false;
|
||||
Pbp = PipelineBindPoint.Graphics;
|
||||
|
||||
if (_bindingBarriersDirty)
|
||||
{
|
||||
// Stale barriers may have been activated by switching program. Emit any that are relevant.
|
||||
_descriptorSetUpdater.InsertBindingBarriers(Cbs);
|
||||
|
||||
_bindingBarriersDirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
Gd.Barriers.Flush(Cbs, _program, RenderPassActive, _rpHolder, EndRenderPassDelegate);
|
||||
Gd.Barriers.Flush(Cbs, _program, _feedbackLoop != 0, RenderPassActive, _rpHolder, EndRenderPassDelegate);
|
||||
|
||||
_descriptorSetUpdater.UpdateAndBindDescriptorSets(Cbs, PipelineBindPoint.Graphics);
|
||||
|
||||
@@ -1628,7 +1724,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
ClearValueCount = 1,
|
||||
};
|
||||
|
||||
Gd.Api.CmdBeginRenderPass(CommandBuffer, renderPassBeginInfo, SubpassContents.Inline);
|
||||
Gd.Api.CmdBeginRenderPass(CommandBuffer, in renderPassBeginInfo, SubpassContents.Inline);
|
||||
RenderPassActive = true;
|
||||
}
|
||||
}
|
||||
|
@@ -116,7 +116,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
DependencyCount = 1,
|
||||
};
|
||||
|
||||
gd.Api.CreateRenderPass(device, renderPassCreateInfo, null, out var renderPass).ThrowOnError();
|
||||
gd.Api.CreateRenderPass(device, in renderPassCreateInfo, null, out var renderPass).ThrowOnError();
|
||||
|
||||
return new DisposableRenderPass(gd.Api, device, renderPass);
|
||||
}
|
||||
|
@@ -1,5 +1,6 @@
|
||||
using Ryujinx.Common.Memory;
|
||||
using Silk.NET.Vulkan;
|
||||
using Silk.NET.Vulkan.Extensions.EXT;
|
||||
|
||||
namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
@@ -21,6 +22,8 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
private Array4<float> _blendConstants;
|
||||
|
||||
private FeedbackLoopAspects _feedbackLoopAspects;
|
||||
|
||||
public uint ViewportsCount;
|
||||
public Array16<Viewport> Viewports;
|
||||
|
||||
@@ -32,7 +35,8 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
Scissor = 1 << 2,
|
||||
Stencil = 1 << 3,
|
||||
Viewport = 1 << 4,
|
||||
All = Blend | DepthBias | Scissor | Stencil | Viewport,
|
||||
FeedbackLoop = 1 << 5,
|
||||
All = Blend | DepthBias | Scissor | Stencil | Viewport | FeedbackLoop,
|
||||
}
|
||||
|
||||
private DirtyFlags _dirty;
|
||||
@@ -99,13 +103,22 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
}
|
||||
}
|
||||
|
||||
public void SetFeedbackLoop(FeedbackLoopAspects aspects)
|
||||
{
|
||||
_feedbackLoopAspects = aspects;
|
||||
|
||||
_dirty |= DirtyFlags.FeedbackLoop;
|
||||
}
|
||||
|
||||
public void ForceAllDirty()
|
||||
{
|
||||
_dirty = DirtyFlags.All;
|
||||
}
|
||||
|
||||
public void ReplayIfDirty(Vk api, CommandBuffer commandBuffer)
|
||||
public void ReplayIfDirty(VulkanRenderer gd, CommandBuffer commandBuffer)
|
||||
{
|
||||
Vk api = gd.Api;
|
||||
|
||||
if (_dirty.HasFlag(DirtyFlags.Blend))
|
||||
{
|
||||
RecordBlend(api, commandBuffer);
|
||||
@@ -131,6 +144,11 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
RecordViewport(api, commandBuffer);
|
||||
}
|
||||
|
||||
if (_dirty.HasFlag(DirtyFlags.FeedbackLoop) && gd.Capabilities.SupportsDynamicAttachmentFeedbackLoop)
|
||||
{
|
||||
RecordFeedbackLoop(gd.DynamicFeedbackLoopApi, commandBuffer);
|
||||
}
|
||||
|
||||
_dirty = DirtyFlags.None;
|
||||
}
|
||||
|
||||
@@ -169,5 +187,17 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
api.CmdSetViewport(commandBuffer, 0, ViewportsCount, Viewports.AsSpan());
|
||||
}
|
||||
}
|
||||
|
||||
private readonly void RecordFeedbackLoop(ExtAttachmentFeedbackLoopDynamicState api, CommandBuffer commandBuffer)
|
||||
{
|
||||
ImageAspectFlags aspects = (_feedbackLoopAspects & FeedbackLoopAspects.Color) != 0 ? ImageAspectFlags.ColorBit : 0;
|
||||
|
||||
if ((_feedbackLoopAspects & FeedbackLoopAspects.Depth) != 0)
|
||||
{
|
||||
aspects |= ImageAspectFlags.DepthBit | ImageAspectFlags.StencilBit;
|
||||
}
|
||||
|
||||
api.CmdSetAttachmentFeedbackLoopEnable(commandBuffer, aspects);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -28,6 +28,8 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
_activeBufferMirrors = new();
|
||||
|
||||
CommandBuffer = (Cbs = gd.CommandBufferPool.Rent()).CommandBuffer;
|
||||
|
||||
IsMainPipeline = true;
|
||||
}
|
||||
|
||||
private void CopyPendingQuery()
|
||||
@@ -235,7 +237,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
if (Pipeline != null && Pbp == PipelineBindPoint.Graphics)
|
||||
{
|
||||
DynamicState.ReplayIfDirty(Gd.Api, CommandBuffer);
|
||||
DynamicState.ReplayIfDirty(Gd, CommandBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -91,7 +91,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
Flags = flags,
|
||||
};
|
||||
|
||||
gd.Api.CreateDescriptorSetLayout(device, descriptorSetLayoutCreateInfo, null, out layouts[setIndex]).ThrowOnError();
|
||||
gd.Api.CreateDescriptorSetLayout(device, in descriptorSetLayoutCreateInfo, null, out layouts[setIndex]).ThrowOnError();
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -8,6 +8,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
struct PipelineState : IDisposable
|
||||
{
|
||||
private const int RequiredSubgroupSize = 32;
|
||||
private const int MaxDynamicStatesCount = 9;
|
||||
|
||||
public PipelineUid Internal;
|
||||
|
||||
@@ -299,6 +300,12 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
set => Internal.Id8 = (Internal.Id8 & 0xFFFFFFFFFFFFFFBF) | ((value ? 1UL : 0UL) << 6);
|
||||
}
|
||||
|
||||
public FeedbackLoopAspects FeedbackLoopAspects
|
||||
{
|
||||
readonly get => (FeedbackLoopAspects)((Internal.Id8 >> 7) & 0x3);
|
||||
set => Internal.Id8 = (Internal.Id8 & 0xFFFFFFFFFFFFFE7F) | (((ulong)value) << 7);
|
||||
}
|
||||
|
||||
public bool HasTessellationControlShader;
|
||||
public NativeArray<PipelineShaderStageCreateInfo> Stages;
|
||||
public PipelineLayout PipelineLayout;
|
||||
@@ -564,9 +571,11 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
}
|
||||
|
||||
bool supportsExtDynamicState = gd.Capabilities.SupportsExtendedDynamicState;
|
||||
int dynamicStatesCount = supportsExtDynamicState ? 8 : 7;
|
||||
bool supportsFeedbackLoopDynamicState = gd.Capabilities.SupportsDynamicAttachmentFeedbackLoop;
|
||||
|
||||
DynamicState* dynamicStates = stackalloc DynamicState[dynamicStatesCount];
|
||||
DynamicState* dynamicStates = stackalloc DynamicState[MaxDynamicStatesCount];
|
||||
|
||||
int dynamicStatesCount = 7;
|
||||
|
||||
dynamicStates[0] = DynamicState.Viewport;
|
||||
dynamicStates[1] = DynamicState.Scissor;
|
||||
@@ -578,7 +587,12 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
if (supportsExtDynamicState)
|
||||
{
|
||||
dynamicStates[7] = DynamicState.VertexInputBindingStrideExt;
|
||||
dynamicStates[dynamicStatesCount++] = DynamicState.VertexInputBindingStrideExt;
|
||||
}
|
||||
|
||||
if (supportsFeedbackLoopDynamicState)
|
||||
{
|
||||
dynamicStates[dynamicStatesCount++] = DynamicState.AttachmentFeedbackLoopEnableExt;
|
||||
}
|
||||
|
||||
var pipelineDynamicStateCreateInfo = new PipelineDynamicStateCreateInfo
|
||||
@@ -588,9 +602,27 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
PDynamicStates = dynamicStates,
|
||||
};
|
||||
|
||||
PipelineCreateFlags flags = 0;
|
||||
|
||||
if (gd.Capabilities.SupportsAttachmentFeedbackLoop)
|
||||
{
|
||||
FeedbackLoopAspects aspects = FeedbackLoopAspects;
|
||||
|
||||
if ((aspects & FeedbackLoopAspects.Color) != 0)
|
||||
{
|
||||
flags |= PipelineCreateFlags.CreateColorAttachmentFeedbackLoopBitExt;
|
||||
}
|
||||
|
||||
if ((aspects & FeedbackLoopAspects.Depth) != 0)
|
||||
{
|
||||
flags |= PipelineCreateFlags.CreateDepthStencilAttachmentFeedbackLoopBitExt;
|
||||
}
|
||||
}
|
||||
|
||||
var pipelineCreateInfo = new GraphicsPipelineCreateInfo
|
||||
{
|
||||
SType = StructureType.GraphicsPipelineCreateInfo,
|
||||
Flags = flags,
|
||||
StageCount = StagesCount,
|
||||
PStages = Stages.Pointer,
|
||||
PVertexInputState = &vertexInputState,
|
||||
|
@@ -52,7 +52,7 @@ namespace Ryujinx.Graphics.Vulkan.Queries
|
||||
PipelineStatistics = flags,
|
||||
};
|
||||
|
||||
gd.Api.CreateQueryPool(device, queryPoolCreateInfo, null, out _queryPool).ThrowOnError();
|
||||
gd.Api.CreateQueryPool(device, in queryPoolCreateInfo, null, out _queryPool).ThrowOnError();
|
||||
}
|
||||
|
||||
var buffer = gd.BufferManager.Create(gd, sizeof(long), forConditionalRendering: true);
|
||||
|
@@ -125,7 +125,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
DependencyCount = 1,
|
||||
};
|
||||
|
||||
gd.Api.CreateRenderPass(device, renderPassCreateInfo, null, out var renderPass).ThrowOnError();
|
||||
gd.Api.CreateRenderPass(device, in renderPassCreateInfo, null, out var renderPass).ThrowOnError();
|
||||
|
||||
_renderPass = new Auto<DisposableRenderPass>(new DisposableRenderPass(gd.Api, device, renderPass));
|
||||
}
|
||||
|
@@ -68,7 +68,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
samplerCreateInfo.BorderColor = BorderColor.FloatCustomExt;
|
||||
}
|
||||
|
||||
gd.Api.CreateSampler(device, samplerCreateInfo, null, out var sampler).ThrowOnError();
|
||||
gd.Api.CreateSampler(device, in samplerCreateInfo, null, out var sampler).ThrowOnError();
|
||||
|
||||
_sampler = new Auto<DisposableSampler>(new DisposableSampler(gd.Api, device, sampler));
|
||||
}
|
||||
|
@@ -64,7 +64,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
PCode = (uint*)pCode,
|
||||
};
|
||||
|
||||
api.CreateShaderModule(device, shaderModuleCreateInfo, null, out _module).ThrowOnError();
|
||||
api.CreateShaderModule(device, in shaderModuleCreateInfo, null, out _module).ThrowOnError();
|
||||
}
|
||||
|
||||
CompileStatus = ProgramLinkStatus.Success;
|
||||
|
@@ -88,7 +88,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
DstOffsets = dstOffsets,
|
||||
};
|
||||
|
||||
api.CmdBlitImage(commandBuffer, srcImage, ImageLayout.General, dstImage, ImageLayout.General, 1, region, filter);
|
||||
api.CmdBlitImage(commandBuffer, srcImage, ImageLayout.General, dstImage, ImageLayout.General, 1, in region, filter);
|
||||
|
||||
copySrcLevel++;
|
||||
copyDstLevel++;
|
||||
@@ -320,13 +320,13 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
var region = new ImageResolve(srcSl, new Offset3D(0, 0, srcZ), dstSl, new Offset3D(0, 0, dstZ), extent);
|
||||
|
||||
api.CmdResolveImage(commandBuffer, srcImage, ImageLayout.General, dstImage, ImageLayout.General, 1, region);
|
||||
api.CmdResolveImage(commandBuffer, srcImage, ImageLayout.General, dstImage, ImageLayout.General, 1, in region);
|
||||
}
|
||||
else
|
||||
{
|
||||
var region = new ImageCopy(srcSl, new Offset3D(0, 0, srcZ), dstSl, new Offset3D(0, 0, dstZ), extent);
|
||||
|
||||
api.CmdCopyImage(commandBuffer, srcImage, ImageLayout.General, dstImage, ImageLayout.General, 1, region);
|
||||
api.CmdCopyImage(commandBuffer, srcImage, ImageLayout.General, dstImage, ImageLayout.General, 1, in region);
|
||||
}
|
||||
|
||||
width = Math.Max(1, width >> 1);
|
||||
@@ -422,7 +422,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
DependencyCount = 1,
|
||||
};
|
||||
|
||||
gd.Api.CreateRenderPass2(device, renderPassCreateInfo, null, out var renderPass).ThrowOnError();
|
||||
gd.Api.CreateRenderPass2(device, in renderPassCreateInfo, null, out var renderPass).ThrowOnError();
|
||||
|
||||
using var rp = new Auto<DisposableRenderPass>(new DisposableRenderPass(gd.Api, device, renderPass));
|
||||
|
||||
@@ -445,7 +445,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
Layers = (uint)src.Layers,
|
||||
};
|
||||
|
||||
gd.Api.CreateFramebuffer(device, framebufferCreateInfo, null, out var framebuffer).ThrowOnError();
|
||||
gd.Api.CreateFramebuffer(device, in framebufferCreateInfo, null, out var framebuffer).ThrowOnError();
|
||||
using var fb = new Auto<DisposableFramebuffer>(new DisposableFramebuffer(gd.Api, device, framebuffer), null, srcView, dstView);
|
||||
|
||||
var renderArea = new Rect2D(null, new Extent2D((uint)src.Info.Width, (uint)src.Info.Height));
|
||||
@@ -465,7 +465,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
// to resolve the depth-stencil texture.
|
||||
// TODO: Do speculative resolve and part of the same render pass as the draw to avoid
|
||||
// ending the current render pass?
|
||||
gd.Api.CmdBeginRenderPass(cbs.CommandBuffer, renderPassBeginInfo, SubpassContents.Inline);
|
||||
gd.Api.CmdBeginRenderPass(cbs.CommandBuffer, in renderPassBeginInfo, SubpassContents.Inline);
|
||||
gd.Api.CmdEndRenderPass(cbs.CommandBuffer);
|
||||
}
|
||||
}
|
||||
|
@@ -4,6 +4,7 @@ using Silk.NET.Vulkan;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Format = Ryujinx.Graphics.GAL.Format;
|
||||
using VkBuffer = Silk.NET.Vulkan.Buffer;
|
||||
using VkFormat = Silk.NET.Vulkan.Format;
|
||||
@@ -12,6 +13,11 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
class TextureStorage : IDisposable
|
||||
{
|
||||
private struct TextureSliceInfo
|
||||
{
|
||||
public int BindCount;
|
||||
}
|
||||
|
||||
private const MemoryPropertyFlags DefaultImageMemoryFlags =
|
||||
MemoryPropertyFlags.DeviceLocalBit;
|
||||
|
||||
@@ -43,6 +49,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
private readonly Image _image;
|
||||
private readonly Auto<DisposableImage> _imageAuto;
|
||||
private readonly Auto<MemoryAllocation> _allocationAuto;
|
||||
private readonly int _depthOrLayers;
|
||||
private Auto<MemoryAllocation> _foreignAllocationAuto;
|
||||
|
||||
private Dictionary<Format, TextureStorage> _aliasedStorages;
|
||||
@@ -55,6 +62,9 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
private int _viewsCount;
|
||||
private readonly ulong _size;
|
||||
|
||||
private int _bindCount;
|
||||
private readonly TextureSliceInfo[] _slices;
|
||||
|
||||
public VkFormat VkFormat { get; }
|
||||
|
||||
public unsafe TextureStorage(
|
||||
@@ -73,6 +83,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
var depth = (uint)(info.Target == Target.Texture3D ? info.Depth : 1);
|
||||
|
||||
VkFormat = format;
|
||||
_depthOrLayers = info.GetDepthOrLayers();
|
||||
|
||||
var type = info.Target.Convert();
|
||||
|
||||
@@ -80,7 +91,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
var sampleCountFlags = ConvertToSampleCountFlags(gd.Capabilities.SupportedSampleCounts, (uint)info.Samples);
|
||||
|
||||
var usage = GetImageUsage(info.Format, info.Target, gd.Capabilities.SupportsShaderStorageImageMultisample);
|
||||
var usage = GetImageUsage(info.Format, info.Target, gd.Capabilities);
|
||||
|
||||
var flags = ImageCreateFlags.CreateMutableFormatBit | ImageCreateFlags.CreateExtendedUsageBit;
|
||||
|
||||
@@ -114,7 +125,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
Flags = flags,
|
||||
};
|
||||
|
||||
gd.Api.CreateImage(device, imageCreateInfo, null, out _image).ThrowOnError();
|
||||
gd.Api.CreateImage(device, in imageCreateInfo, null, out _image).ThrowOnError();
|
||||
|
||||
if (foreignAllocation == null)
|
||||
{
|
||||
@@ -148,6 +159,8 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
InitialTransition(ImageLayout.Preinitialized, ImageLayout.General);
|
||||
}
|
||||
|
||||
_slices = new TextureSliceInfo[levels * _depthOrLayers];
|
||||
}
|
||||
|
||||
public TextureStorage CreateAliasedColorForDepthStorageUnsafe(Format format)
|
||||
@@ -284,7 +297,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
0,
|
||||
null,
|
||||
1,
|
||||
barrier);
|
||||
in barrier);
|
||||
|
||||
if (useTempCbs)
|
||||
{
|
||||
@@ -292,7 +305,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
}
|
||||
}
|
||||
|
||||
public static ImageUsageFlags GetImageUsage(Format format, Target target, bool supportsMsStorage)
|
||||
public static ImageUsageFlags GetImageUsage(Format format, Target target, in HardwareCapabilities capabilities)
|
||||
{
|
||||
var usage = DefaultUsageFlags;
|
||||
|
||||
@@ -305,11 +318,19 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
usage |= ImageUsageFlags.ColorAttachmentBit;
|
||||
}
|
||||
|
||||
bool supportsMsStorage = capabilities.SupportsShaderStorageImageMultisample;
|
||||
|
||||
if (format.IsImageCompatible() && (supportsMsStorage || !target.IsMultisample()))
|
||||
{
|
||||
usage |= ImageUsageFlags.StorageBit;
|
||||
}
|
||||
|
||||
if (capabilities.SupportsAttachmentFeedbackLoop &&
|
||||
(usage & (ImageUsageFlags.DepthStencilAttachmentBit | ImageUsageFlags.ColorAttachmentBit)) != 0)
|
||||
{
|
||||
usage |= ImageUsageFlags.AttachmentFeedbackLoopBitExt;
|
||||
}
|
||||
|
||||
return usage;
|
||||
}
|
||||
|
||||
@@ -401,11 +422,11 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
if (to)
|
||||
{
|
||||
_gd.Api.CmdCopyImageToBuffer(commandBuffer, image, ImageLayout.General, buffer, 1, region);
|
||||
_gd.Api.CmdCopyImageToBuffer(commandBuffer, image, ImageLayout.General, buffer, 1, in region);
|
||||
}
|
||||
else
|
||||
{
|
||||
_gd.Api.CmdCopyBufferToImage(commandBuffer, buffer, image, ImageLayout.General, 1, region);
|
||||
_gd.Api.CmdCopyBufferToImage(commandBuffer, buffer, image, ImageLayout.General, 1, in region);
|
||||
}
|
||||
|
||||
offset += mipSize;
|
||||
@@ -510,6 +531,55 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
}
|
||||
}
|
||||
|
||||
public void AddBinding(TextureView view)
|
||||
{
|
||||
// Assumes a view only has a first level.
|
||||
|
||||
int index = view.FirstLevel * _depthOrLayers + view.FirstLayer;
|
||||
int layers = view.Layers;
|
||||
|
||||
for (int i = 0; i < layers; i++)
|
||||
{
|
||||
ref TextureSliceInfo info = ref _slices[index++];
|
||||
|
||||
info.BindCount++;
|
||||
}
|
||||
|
||||
_bindCount++;
|
||||
}
|
||||
|
||||
public void ClearBindings()
|
||||
{
|
||||
if (_bindCount != 0)
|
||||
{
|
||||
Array.Clear(_slices, 0, _slices.Length);
|
||||
|
||||
_bindCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool IsBound(TextureView view)
|
||||
{
|
||||
if (_bindCount != 0)
|
||||
{
|
||||
int index = view.FirstLevel * _depthOrLayers + view.FirstLayer;
|
||||
int layers = view.Layers;
|
||||
|
||||
for (int i = 0; i < layers; i++)
|
||||
{
|
||||
ref TextureSliceInfo info = ref _slices[index++];
|
||||
|
||||
if (info.BindCount != 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void IncrementViewsCount()
|
||||
{
|
||||
_viewsCount++;
|
||||
|
@@ -23,6 +23,8 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
private readonly Auto<DisposableImageView> _imageView2dArray;
|
||||
private Dictionary<Format, TextureView> _selfManagedViews;
|
||||
|
||||
private int _hazardUses;
|
||||
|
||||
private readonly TextureCreateInfo _info;
|
||||
|
||||
private HashTableSlim<RenderPassCacheKey, RenderPassHolder> _renderPasses;
|
||||
@@ -60,7 +62,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
gd.Textures.Add(this);
|
||||
|
||||
var format = _gd.FormatCapabilities.ConvertToVkFormat(info.Format);
|
||||
var usage = TextureStorage.GetImageUsage(info.Format, info.Target, gd.Capabilities.SupportsShaderStorageImageMultisample);
|
||||
var usage = TextureStorage.GetImageUsage(info.Format, info.Target, gd.Capabilities);
|
||||
var levels = (uint)info.Levels;
|
||||
var layers = (uint)info.GetLayers();
|
||||
|
||||
@@ -117,7 +119,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
PNext = &imageViewUsage,
|
||||
};
|
||||
|
||||
gd.Api.CreateImageView(device, imageCreateInfo, null, out var imageView).ThrowOnError();
|
||||
gd.Api.CreateImageView(device, in imageCreateInfo, null, out var imageView).ThrowOnError();
|
||||
return new Auto<DisposableImageView>(new DisposableImageView(gd.Api, device, imageView), null, storage.GetImage());
|
||||
}
|
||||
|
||||
@@ -492,7 +494,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
dstStageMask,
|
||||
DependencyFlags.None,
|
||||
1,
|
||||
memoryBarrier,
|
||||
in memoryBarrier,
|
||||
0,
|
||||
null,
|
||||
0,
|
||||
@@ -557,7 +559,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
0,
|
||||
null,
|
||||
1,
|
||||
memoryBarrier);
|
||||
in memoryBarrier);
|
||||
}
|
||||
|
||||
public TextureView GetView(Format format)
|
||||
@@ -949,11 +951,11 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
if (to)
|
||||
{
|
||||
_gd.Api.CmdCopyImageToBuffer(commandBuffer, image, ImageLayout.General, buffer, 1, region);
|
||||
_gd.Api.CmdCopyImageToBuffer(commandBuffer, image, ImageLayout.General, buffer, 1, in region);
|
||||
}
|
||||
else
|
||||
{
|
||||
_gd.Api.CmdCopyBufferToImage(commandBuffer, buffer, image, ImageLayout.General, 1, region);
|
||||
_gd.Api.CmdCopyBufferToImage(commandBuffer, buffer, image, ImageLayout.General, 1, in region);
|
||||
}
|
||||
|
||||
offset += mipSize;
|
||||
@@ -1010,11 +1012,11 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
if (to)
|
||||
{
|
||||
_gd.Api.CmdCopyImageToBuffer(commandBuffer, image, ImageLayout.General, buffer, 1, region);
|
||||
_gd.Api.CmdCopyImageToBuffer(commandBuffer, image, ImageLayout.General, buffer, 1, in region);
|
||||
}
|
||||
else
|
||||
{
|
||||
_gd.Api.CmdCopyBufferToImage(commandBuffer, buffer, image, ImageLayout.General, 1, region);
|
||||
_gd.Api.CmdCopyBufferToImage(commandBuffer, buffer, image, ImageLayout.General, 1, in region);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1034,6 +1036,34 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void PrepareForUsage(CommandBufferScoped cbs, PipelineStageFlags flags, List<TextureView> feedbackLoopHazards)
|
||||
{
|
||||
Storage.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, flags);
|
||||
|
||||
if (feedbackLoopHazards != null && Storage.IsBound(this))
|
||||
{
|
||||
feedbackLoopHazards.Add(this);
|
||||
_hazardUses++;
|
||||
}
|
||||
}
|
||||
|
||||
public void ClearUsage(List<TextureView> feedbackLoopHazards)
|
||||
{
|
||||
if (_hazardUses != 0 && feedbackLoopHazards != null)
|
||||
{
|
||||
feedbackLoopHazards.Remove(this);
|
||||
_hazardUses--;
|
||||
}
|
||||
}
|
||||
|
||||
public void DecrementHazardUses()
|
||||
{
|
||||
if (_hazardUses != 0)
|
||||
{
|
||||
_hazardUses--;
|
||||
}
|
||||
}
|
||||
|
||||
public (RenderPassHolder rpHolder, Auto<DisposableFramebuffer> framebuffer) GetPassAndFramebuffer(
|
||||
VulkanRenderer gd,
|
||||
Device device,
|
||||
|
@@ -90,11 +90,9 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
DriverId.SamsungProprietary => "Samsung",
|
||||
DriverId.MesaVenus => "Venus",
|
||||
DriverId.MesaDozen => "Dozen",
|
||||
|
||||
// TODO: Use real enum when we have an up to date Silk.NET.
|
||||
(DriverId)24 => "NVK",
|
||||
(DriverId)25 => "Imagination (Open)",
|
||||
(DriverId)26 => "Honeykrisp",
|
||||
DriverId.MesaNvk => "NVK",
|
||||
DriverId.ImaginationOpenSourceMesa => "Imagination (Open)",
|
||||
DriverId.MesaAgxv => "Honeykrisp",
|
||||
_ => id.ToString(),
|
||||
};
|
||||
}
|
||||
|
@@ -44,6 +44,8 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
"VK_EXT_4444_formats",
|
||||
"VK_KHR_8bit_storage",
|
||||
"VK_KHR_maintenance2",
|
||||
"VK_EXT_attachment_feedback_loop_layout",
|
||||
"VK_EXT_attachment_feedback_loop_dynamic_state",
|
||||
};
|
||||
|
||||
private static readonly string[] _requiredExtensions = {
|
||||
@@ -357,6 +359,28 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
features2.PNext = &supportedFeaturesDepthClipControl;
|
||||
}
|
||||
|
||||
PhysicalDeviceAttachmentFeedbackLoopLayoutFeaturesEXT supportedFeaturesAttachmentFeedbackLoopLayout = new()
|
||||
{
|
||||
SType = StructureType.PhysicalDeviceAttachmentFeedbackLoopLayoutFeaturesExt,
|
||||
PNext = features2.PNext,
|
||||
};
|
||||
|
||||
if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_attachment_feedback_loop_layout"))
|
||||
{
|
||||
features2.PNext = &supportedFeaturesAttachmentFeedbackLoopLayout;
|
||||
}
|
||||
|
||||
PhysicalDeviceAttachmentFeedbackLoopDynamicStateFeaturesEXT supportedFeaturesDynamicAttachmentFeedbackLoopLayout = new()
|
||||
{
|
||||
SType = StructureType.PhysicalDeviceAttachmentFeedbackLoopDynamicStateFeaturesExt,
|
||||
PNext = features2.PNext,
|
||||
};
|
||||
|
||||
if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_attachment_feedback_loop_dynamic_state"))
|
||||
{
|
||||
features2.PNext = &supportedFeaturesDynamicAttachmentFeedbackLoopLayout;
|
||||
}
|
||||
|
||||
PhysicalDeviceVulkan12Features supportedPhysicalDeviceVulkan12Features = new()
|
||||
{
|
||||
SType = StructureType.PhysicalDeviceVulkan12Features,
|
||||
@@ -531,6 +555,36 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
pExtendedFeatures = &featuresDepthClipControl;
|
||||
}
|
||||
|
||||
PhysicalDeviceAttachmentFeedbackLoopLayoutFeaturesEXT featuresAttachmentFeedbackLoopLayout;
|
||||
|
||||
if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_attachment_feedback_loop_layout") &&
|
||||
supportedFeaturesAttachmentFeedbackLoopLayout.AttachmentFeedbackLoopLayout)
|
||||
{
|
||||
featuresAttachmentFeedbackLoopLayout = new()
|
||||
{
|
||||
SType = StructureType.PhysicalDeviceAttachmentFeedbackLoopLayoutFeaturesExt,
|
||||
PNext = pExtendedFeatures,
|
||||
AttachmentFeedbackLoopLayout = true,
|
||||
};
|
||||
|
||||
pExtendedFeatures = &featuresAttachmentFeedbackLoopLayout;
|
||||
}
|
||||
|
||||
PhysicalDeviceAttachmentFeedbackLoopDynamicStateFeaturesEXT featuresDynamicAttachmentFeedbackLoopLayout;
|
||||
|
||||
if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_attachment_feedback_loop_dynamic_state") &&
|
||||
supportedFeaturesDynamicAttachmentFeedbackLoopLayout.AttachmentFeedbackLoopDynamicState)
|
||||
{
|
||||
featuresDynamicAttachmentFeedbackLoopLayout = new()
|
||||
{
|
||||
SType = StructureType.PhysicalDeviceAttachmentFeedbackLoopDynamicStateFeaturesExt,
|
||||
PNext = pExtendedFeatures,
|
||||
AttachmentFeedbackLoopDynamicState = true,
|
||||
};
|
||||
|
||||
pExtendedFeatures = &featuresDynamicAttachmentFeedbackLoopLayout;
|
||||
}
|
||||
|
||||
var enabledExtensions = _requiredExtensions.Union(_desirableExtensions.Intersect(physicalDevice.DeviceExtensions)).ToArray();
|
||||
|
||||
IntPtr* ppEnabledExtensions = stackalloc IntPtr[enabledExtensions.Length];
|
||||
|
@@ -38,6 +38,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
internal KhrPushDescriptor PushDescriptorApi { get; private set; }
|
||||
internal ExtTransformFeedback TransformFeedbackApi { get; private set; }
|
||||
internal KhrDrawIndirectCount DrawIndirectCountApi { get; private set; }
|
||||
internal ExtAttachmentFeedbackLoopDynamicState DynamicFeedbackLoopApi { get; private set; }
|
||||
|
||||
internal uint QueueFamilyIndex { get; private set; }
|
||||
internal Queue Queue { get; private set; }
|
||||
@@ -149,6 +150,11 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
DrawIndirectCountApi = drawIndirectCountApi;
|
||||
}
|
||||
|
||||
if (Api.TryGetDeviceExtension(_instance.Instance, _device, out ExtAttachmentFeedbackLoopDynamicState dynamicFeedbackLoopApi))
|
||||
{
|
||||
DynamicFeedbackLoopApi = dynamicFeedbackLoopApi;
|
||||
}
|
||||
|
||||
if (maxQueueCount >= 2)
|
||||
{
|
||||
Api.GetDeviceQueue(_device, queueFamilyIndex, 1, out var backgroundQueue);
|
||||
@@ -243,6 +249,16 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
SType = StructureType.PhysicalDeviceDepthClipControlFeaturesExt,
|
||||
};
|
||||
|
||||
PhysicalDeviceAttachmentFeedbackLoopLayoutFeaturesEXT featuresAttachmentFeedbackLoop = new()
|
||||
{
|
||||
SType = StructureType.PhysicalDeviceAttachmentFeedbackLoopLayoutFeaturesExt,
|
||||
};
|
||||
|
||||
PhysicalDeviceAttachmentFeedbackLoopDynamicStateFeaturesEXT featuresDynamicAttachmentFeedbackLoop = new()
|
||||
{
|
||||
SType = StructureType.PhysicalDeviceAttachmentFeedbackLoopDynamicStateFeaturesExt,
|
||||
};
|
||||
|
||||
PhysicalDevicePortabilitySubsetFeaturesKHR featuresPortabilitySubset = new()
|
||||
{
|
||||
SType = StructureType.PhysicalDevicePortabilitySubsetFeaturesKhr,
|
||||
@@ -279,6 +295,22 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
features2.PNext = &featuresDepthClipControl;
|
||||
}
|
||||
|
||||
bool supportsAttachmentFeedbackLoop = _physicalDevice.IsDeviceExtensionPresent("VK_EXT_attachment_feedback_loop_layout");
|
||||
|
||||
if (supportsAttachmentFeedbackLoop)
|
||||
{
|
||||
featuresAttachmentFeedbackLoop.PNext = features2.PNext;
|
||||
features2.PNext = &featuresAttachmentFeedbackLoop;
|
||||
}
|
||||
|
||||
bool supportsDynamicAttachmentFeedbackLoop = _physicalDevice.IsDeviceExtensionPresent("VK_EXT_attachment_feedback_loop_dynamic_state");
|
||||
|
||||
if (supportsDynamicAttachmentFeedbackLoop)
|
||||
{
|
||||
featuresDynamicAttachmentFeedbackLoop.PNext = features2.PNext;
|
||||
features2.PNext = &featuresDynamicAttachmentFeedbackLoop;
|
||||
}
|
||||
|
||||
bool usePortability = _physicalDevice.IsDeviceExtensionPresent("VK_KHR_portability_subset");
|
||||
|
||||
if (usePortability)
|
||||
@@ -401,6 +433,8 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
_physicalDevice.IsDeviceExtensionPresent("VK_NV_viewport_array2"),
|
||||
_physicalDevice.IsDeviceExtensionPresent(ExtExternalMemoryHost.ExtensionName),
|
||||
supportsDepthClipControl && featuresDepthClipControl.DepthClipControl,
|
||||
supportsAttachmentFeedbackLoop && featuresAttachmentFeedbackLoop.AttachmentFeedbackLoopLayout,
|
||||
supportsDynamicAttachmentFeedbackLoop && featuresDynamicAttachmentFeedbackLoop.AttachmentFeedbackLoopDynamicState,
|
||||
propertiesSubgroup.SubgroupSize,
|
||||
supportedSampleCounts,
|
||||
portabilityFlags,
|
||||
|
@@ -160,7 +160,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
SwizzleComponent.Blue,
|
||||
SwizzleComponent.Alpha);
|
||||
|
||||
_gd.SwapchainApi.CreateSwapchain(_device, swapchainCreateInfo, null, out _swapchain).ThrowOnError();
|
||||
_gd.SwapchainApi.CreateSwapchain(_device, in swapchainCreateInfo, null, out _swapchain).ThrowOnError();
|
||||
|
||||
_gd.SwapchainApi.GetSwapchainImages(_device, _swapchain, &imageCount, null);
|
||||
|
||||
@@ -187,14 +187,14 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
for (int i = 0; i < _imageAvailableSemaphores.Length; i++)
|
||||
{
|
||||
_gd.Api.CreateSemaphore(_device, semaphoreCreateInfo, null, out _imageAvailableSemaphores[i]).ThrowOnError();
|
||||
_gd.Api.CreateSemaphore(_device, in semaphoreCreateInfo, null, out _imageAvailableSemaphores[i]).ThrowOnError();
|
||||
}
|
||||
|
||||
_renderFinishedSemaphores = new Semaphore[imageCount];
|
||||
|
||||
for (int i = 0; i < _renderFinishedSemaphores.Length; i++)
|
||||
{
|
||||
_gd.Api.CreateSemaphore(_device, semaphoreCreateInfo, null, out _renderFinishedSemaphores[i]).ThrowOnError();
|
||||
_gd.Api.CreateSemaphore(_device, in semaphoreCreateInfo, null, out _renderFinishedSemaphores[i]).ThrowOnError();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -220,7 +220,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
SubresourceRange = subresourceRange,
|
||||
};
|
||||
|
||||
_gd.Api.CreateImageView(_device, imageCreateInfo, null, out var imageView).ThrowOnError();
|
||||
_gd.Api.CreateImageView(_device, in imageCreateInfo, null, out var imageView).ThrowOnError();
|
||||
|
||||
return new TextureView(_gd, _device, new DisposableImageView(_gd.Api, _device, imageView), info, format);
|
||||
}
|
||||
@@ -479,7 +479,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
lock (_gd.QueueLock)
|
||||
{
|
||||
_gd.SwapchainApi.QueuePresent(_gd.Queue, presentInfo);
|
||||
_gd.SwapchainApi.QueuePresent(_gd.Queue, in presentInfo);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -611,7 +611,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
0,
|
||||
null,
|
||||
1,
|
||||
barrier);
|
||||
in barrier);
|
||||
}
|
||||
|
||||
private void CaptureFrame(TextureView texture, int x, int y, int width, int height, bool isBgra, bool flipX, bool flipY)
|
||||
|
@@ -4,6 +4,8 @@ using Ryujinx.Common.Configuration;
|
||||
using Ryujinx.Common.GraphicsDriver;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Common.SystemInterop;
|
||||
using Ryujinx.Common.Utilities;
|
||||
using Ryujinx.Graphics.Vulkan.MoltenVK;
|
||||
using Ryujinx.Modules;
|
||||
using Ryujinx.SDL2.Common;
|
||||
using Ryujinx.UI;
|
||||
@@ -40,9 +42,6 @@ namespace Ryujinx
|
||||
[LibraryImport("user32.dll", SetLastError = true)]
|
||||
public static partial int MessageBoxA(IntPtr hWnd, [MarshalAs(UnmanagedType.LPStr)] string text, [MarshalAs(UnmanagedType.LPStr)] string caption, uint type);
|
||||
|
||||
[LibraryImport("libc", SetLastError = true)]
|
||||
private static partial int setenv([MarshalAs(UnmanagedType.LPStr)] string name, [MarshalAs(UnmanagedType.LPStr)] string value, int overwrite);
|
||||
|
||||
private const uint MbIconWarning = 0x30;
|
||||
|
||||
static Program()
|
||||
@@ -104,12 +103,13 @@ namespace Ryujinx
|
||||
throw new NotSupportedException("Failed to initialize multi-threading support.");
|
||||
}
|
||||
|
||||
Environment.SetEnvironmentVariable("GDK_BACKEND", "x11");
|
||||
setenv("GDK_BACKEND", "x11", 1);
|
||||
OsUtils.SetEnvironmentVariableNoCaching("GDK_BACKEND", "x11");
|
||||
}
|
||||
|
||||
if (OperatingSystem.IsMacOS())
|
||||
{
|
||||
MVKInitialization.InitializeResolver();
|
||||
|
||||
string baseDirectory = Path.GetDirectoryName(AppDomain.CurrentDomain.BaseDirectory);
|
||||
string resourcesDataDir;
|
||||
|
||||
@@ -122,19 +122,13 @@ namespace Ryujinx
|
||||
resourcesDataDir = baseDirectory;
|
||||
}
|
||||
|
||||
static void SetEnvironmentVariableNoCaching(string key, string value)
|
||||
{
|
||||
int res = setenv(key, value, 1);
|
||||
Debug.Assert(res != -1);
|
||||
}
|
||||
|
||||
// On macOS, GTK3 needs XDG_DATA_DIRS to be set, otherwise it will try searching for "gschemas.compiled" in system directories.
|
||||
SetEnvironmentVariableNoCaching("XDG_DATA_DIRS", Path.Combine(resourcesDataDir, "share"));
|
||||
OsUtils.SetEnvironmentVariableNoCaching("XDG_DATA_DIRS", Path.Combine(resourcesDataDir, "share"));
|
||||
|
||||
// On macOS, GTK3 needs GDK_PIXBUF_MODULE_FILE to be set, otherwise it will try searching for "loaders.cache" in system directories.
|
||||
SetEnvironmentVariableNoCaching("GDK_PIXBUF_MODULE_FILE", Path.Combine(resourcesDataDir, "lib", "gdk-pixbuf-2.0", "2.10.0", "loaders.cache"));
|
||||
OsUtils.SetEnvironmentVariableNoCaching("GDK_PIXBUF_MODULE_FILE", Path.Combine(resourcesDataDir, "lib", "gdk-pixbuf-2.0", "2.10.0", "loaders.cache"));
|
||||
|
||||
SetEnvironmentVariableNoCaching("GTK_IM_MODULE_FILE", Path.Combine(resourcesDataDir, "lib", "gtk-3.0", "3.0.0", "immodules.cache"));
|
||||
OsUtils.SetEnvironmentVariableNoCaching("GTK_IM_MODULE_FILE", Path.Combine(resourcesDataDir, "lib", "gtk-3.0", "3.0.0", "immodules.cache"));
|
||||
}
|
||||
|
||||
string systemPath = Environment.GetEnvironmentVariable("Path", EnvironmentVariableTarget.Machine);
|
||||
@@ -230,9 +224,9 @@ namespace Ryujinx
|
||||
// Logging system information.
|
||||
PrintSystemInfo();
|
||||
|
||||
// Enable OGL multithreading on the driver, when available.
|
||||
// Enable OGL multithreading on the driver, and some other flags.
|
||||
BackendThreading threadingMode = ConfigurationState.Instance.Graphics.BackendThreading;
|
||||
DriverUtilities.ToggleOGLThreading(threadingMode == BackendThreading.Off);
|
||||
DriverUtilities.InitDriverConfig(threadingMode == BackendThreading.Off);
|
||||
|
||||
// Initialize Gtk.
|
||||
Application.Init();
|
||||
|
@@ -7,6 +7,7 @@ using Ryujinx.Common.Configuration.Hid;
|
||||
using Ryujinx.Common.Configuration.Hid.Controller;
|
||||
using Ryujinx.Common.Configuration.Hid.Controller.Motion;
|
||||
using Ryujinx.Common.Configuration.Hid.Keyboard;
|
||||
using Ryujinx.Common.GraphicsDriver;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Common.Logging.Targets;
|
||||
using Ryujinx.Common.SystemInterop;
|
||||
@@ -18,6 +19,7 @@ using Ryujinx.Graphics.Gpu;
|
||||
using Ryujinx.Graphics.Gpu.Shader;
|
||||
using Ryujinx.Graphics.OpenGL;
|
||||
using Ryujinx.Graphics.Vulkan;
|
||||
using Ryujinx.Graphics.Vulkan.MoltenVK;
|
||||
using Ryujinx.Headless.SDL2.OpenGL;
|
||||
using Ryujinx.Headless.SDL2.Vulkan;
|
||||
using Ryujinx.HLE;
|
||||
@@ -88,6 +90,11 @@ namespace Ryujinx.Headless.SDL2
|
||||
};
|
||||
}
|
||||
|
||||
if (OperatingSystem.IsMacOS())
|
||||
{
|
||||
MVKInitialization.InitializeResolver();
|
||||
}
|
||||
|
||||
Parser.Default.ParseArguments<Options>(args)
|
||||
.WithParsed(Load)
|
||||
.WithNotParsed(errors => errors.Output());
|
||||
@@ -457,6 +464,8 @@ namespace Ryujinx.Headless.SDL2
|
||||
GraphicsConfig.ShadersDumpPath = option.GraphicsShadersDumpPath;
|
||||
GraphicsConfig.EnableMacroHLE = !option.DisableMacroHLE;
|
||||
|
||||
DriverUtilities.InitDriverConfig(option.BackendThreading == BackendThreading.Off);
|
||||
|
||||
while (true)
|
||||
{
|
||||
LoadApplication(option);
|
||||
|
@@ -7,6 +7,7 @@ using Ryujinx.Common.Configuration;
|
||||
using Ryujinx.Common.GraphicsDriver;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Common.SystemInterop;
|
||||
using Ryujinx.Graphics.Vulkan.MoltenVK;
|
||||
using Ryujinx.Modules;
|
||||
using Ryujinx.SDL2.Common;
|
||||
using Ryujinx.UI.Common;
|
||||
@@ -80,6 +81,11 @@ namespace Ryujinx.Ava
|
||||
// Parse arguments
|
||||
CommandLineState.ParseArguments(args);
|
||||
|
||||
if (OperatingSystem.IsMacOS())
|
||||
{
|
||||
MVKInitialization.InitializeResolver();
|
||||
}
|
||||
|
||||
// Delete backup files after updating.
|
||||
Task.Run(Updater.CleanupUpdate);
|
||||
|
||||
@@ -111,8 +117,8 @@ namespace Ryujinx.Ava
|
||||
// Logging system information.
|
||||
PrintSystemInfo();
|
||||
|
||||
// Enable OGL multithreading on the driver, when available.
|
||||
DriverUtilities.ToggleOGLThreading(ConfigurationState.Instance.Graphics.BackendThreading == BackendThreading.Off);
|
||||
// Enable OGL multithreading on the driver, and some other flags.
|
||||
DriverUtilities.InitDriverConfig(ConfigurationState.Instance.Graphics.BackendThreading == BackendThreading.Off);
|
||||
|
||||
// Check if keys exists.
|
||||
if (!File.Exists(Path.Combine(AppDataManager.KeysDirPath, "prod.keys")))
|
||||
|
@@ -164,7 +164,7 @@ return view.extend({
|
||||
o.value('', '---');
|
||||
o.value('223.5.5.5', _('Aliyun Public DNS (223.5.5.5)'));
|
||||
o.value('119.29.29.29', _('Tencent Public DNS (119.29.29.29)'));
|
||||
o.value('114.114.114.114', _('Xinfeng Public DNS (114.114.114.114)'));
|
||||
o.value('117.50.10.10', _('ThreatBook Public DNS (117.50.10.10)'));
|
||||
o.default = '8.8.8.8';
|
||||
o.rmempty = false;
|
||||
o.depends({'routing_mode': 'custom', '!reverse': true});
|
||||
@@ -187,7 +187,7 @@ return view.extend({
|
||||
o.value('223.5.5.5', _('Aliyun Public DNS (223.5.5.5)'));
|
||||
o.value('210.2.4.8', _('CNNIC Public DNS (210.2.4.8)'));
|
||||
o.value('119.29.29.29', _('Tencent Public DNS (119.29.29.29)'));
|
||||
o.value('114.114.114.114', _('Xinfeng Public DNS (114.114.114.114)'));
|
||||
o.value('117.50.10.10', _('ThreatBook Public DNS (117.50.10.10)'));
|
||||
o.depends('routing_mode', 'bypass_mainland_china');
|
||||
o.validate = function(section_id) {
|
||||
if (section_id) {
|
||||
|
@@ -1152,6 +1152,15 @@ return view.extend({
|
||||
var routing_mode = uci.get(data[0], 'config', 'routing_mode');
|
||||
var features = data[1];
|
||||
|
||||
/* Cache subscription information, it will be called multiple times */
|
||||
var subinfo = [];
|
||||
for (var suburl of (uci.get(data[0], 'subscription', 'subscription_url') || [])) {
|
||||
const url = new URL(suburl);
|
||||
const urlhash = hp.calcStringMD5(suburl.replace(/#.*$/, ''));
|
||||
const title = url.hash ? decodeURIComponent(url.hash.slice(1)) : url.hostname;
|
||||
subinfo.push({ 'hash': urlhash, 'title': title });
|
||||
}
|
||||
|
||||
m = new form.Map('homeproxy', _('Edit nodes'));
|
||||
|
||||
s = m.section(form.NamedSection, 'subscription', 'homeproxy');
|
||||
@@ -1163,7 +1172,11 @@ return view.extend({
|
||||
ss = renderNodeSettings(o.subsection, data, features, main_node, routing_mode);
|
||||
ss.addremove = true;
|
||||
ss.filter = function(section_id) {
|
||||
return uci.get(data[0], section_id, 'grouphash') ? false : true;
|
||||
for (var info of subinfo)
|
||||
if (info.hash === uci.get(data[0], section_id, 'grouphash'))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
/* Import subscription links start */
|
||||
/* Thanks to luci-app-shadowsocks-libev */
|
||||
@@ -1226,7 +1239,7 @@ return view.extend({
|
||||
])
|
||||
])
|
||||
}
|
||||
ss.renderSectionAdd = function(extra_class) {
|
||||
ss.renderSectionAdd = function(/* ... */) {
|
||||
var el = form.GridSection.prototype.renderSectionAdd.apply(this, arguments),
|
||||
nameEl = el.querySelector('.cbi-section-create-name');
|
||||
|
||||
@@ -1258,16 +1271,12 @@ return view.extend({
|
||||
/* User nodes end */
|
||||
|
||||
/* Subscription nodes start */
|
||||
for (var suburl of (uci.get(data[0], 'subscription', 'subscription_url') || [])) {
|
||||
const url = new URL(suburl);
|
||||
const urlhash = hp.calcStringMD5(suburl.replace(/#.*$/, ''));
|
||||
const title = url.hash ? decodeURIComponent(url.hash.slice(1)) : url.hostname;
|
||||
|
||||
s.tab('sub_' + urlhash, _('Sub (%s)').format(title));
|
||||
o = s.taboption('sub_' + urlhash, form.SectionValue, '_sub_' + urlhash, form.GridSection, 'node');
|
||||
for (const info of subinfo) {
|
||||
s.tab('sub_' + info.hash, _('Sub (%s)').format(info.title));
|
||||
o = s.taboption('sub_' + info.hash, form.SectionValue, '_sub_' + info.hash, form.GridSection, 'node');
|
||||
ss = renderNodeSettings(o.subsection, data, features, main_node, routing_mode);
|
||||
ss.filter = function(section_id) {
|
||||
return (uci.get(data[0], section_id, 'grouphash') === urlhash);
|
||||
return (uci.get(data[0], section_id, 'grouphash') === info.hash);
|
||||
}
|
||||
}
|
||||
/* Subscription nodes end */
|
||||
|
@@ -5,7 +5,7 @@ msgstr "Content-Type: text/plain; charset=UTF-8"
|
||||
msgid "%s log"
|
||||
msgstr ""
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1399
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1408
|
||||
msgid "%s nodes removed"
|
||||
msgstr ""
|
||||
|
||||
@@ -99,7 +99,7 @@ msgid "All ports"
|
||||
msgstr ""
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:982
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1325
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1334
|
||||
msgid "Allow insecure"
|
||||
msgstr ""
|
||||
|
||||
@@ -107,7 +107,7 @@ msgstr ""
|
||||
msgid "Allow insecure connection at TLS client."
|
||||
msgstr ""
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1326
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1335
|
||||
msgid "Allow insecure connection by default when add nodes from subscriptions."
|
||||
msgstr ""
|
||||
|
||||
@@ -137,7 +137,7 @@ msgstr ""
|
||||
msgid "Alternative TLS port"
|
||||
msgstr ""
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1362
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1371
|
||||
msgid "An error occurred during updating subscriptions: %s"
|
||||
msgstr ""
|
||||
|
||||
@@ -193,11 +193,11 @@ msgstr ""
|
||||
msgid "Auto configure firewall"
|
||||
msgstr ""
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1279
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1288
|
||||
msgid "Auto update"
|
||||
msgstr ""
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1280
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1289
|
||||
msgid "Auto update subscriptions."
|
||||
msgstr ""
|
||||
|
||||
@@ -232,7 +232,7 @@ msgid ""
|
||||
"Bind outbound traffic to specific interface. Leave empty to auto detect."
|
||||
msgstr ""
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1315
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1324
|
||||
msgid "Blacklist mode"
|
||||
msgstr ""
|
||||
|
||||
@@ -279,7 +279,7 @@ msgstr ""
|
||||
msgid "CUBIC"
|
||||
msgstr ""
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1179
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1192
|
||||
msgid "Cancel"
|
||||
msgstr ""
|
||||
|
||||
@@ -426,7 +426,7 @@ msgstr ""
|
||||
msgid "Default outbound"
|
||||
msgstr ""
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1333
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1342
|
||||
msgid "Default packet encoding"
|
||||
msgstr ""
|
||||
|
||||
@@ -470,7 +470,7 @@ msgstr ""
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:492
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:504
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1061
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1314
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1323
|
||||
#: htdocs/luci-static/resources/view/homeproxy/server.js:246
|
||||
#: htdocs/luci-static/resources/view/homeproxy/server.js:258
|
||||
msgid "Disable"
|
||||
@@ -555,14 +555,14 @@ msgstr ""
|
||||
msgid "Download bandwidth in Mbps."
|
||||
msgstr ""
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1321
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1330
|
||||
msgid ""
|
||||
"Drop/keep nodes that contain the specific keywords. <a target=\"_blank\" "
|
||||
"href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/"
|
||||
"Regular_Expressions\">Regex</a> is supported."
|
||||
msgstr ""
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1313
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1322
|
||||
msgid "Drop/keep specific nodes from subscriptions."
|
||||
msgstr ""
|
||||
|
||||
@@ -604,7 +604,7 @@ msgstr ""
|
||||
msgid "Early data is sent in path instead of header by default."
|
||||
msgstr ""
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1155
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1164
|
||||
msgid "Edit nodes"
|
||||
msgstr ""
|
||||
|
||||
@@ -705,9 +705,9 @@ msgstr ""
|
||||
#: htdocs/luci-static/resources/view/homeproxy/client.js:1200
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:452
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1082
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1242
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1302
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1305
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1255
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1311
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1314
|
||||
#: htdocs/luci-static/resources/view/homeproxy/server.js:211
|
||||
#: htdocs/luci-static/resources/view/homeproxy/server.js:602
|
||||
#: htdocs/luci-static/resources/view/homeproxy/server.js:604
|
||||
@@ -730,11 +730,11 @@ msgstr ""
|
||||
msgid "Failed to upload %s, error: %s."
|
||||
msgstr ""
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1320
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1329
|
||||
msgid "Filter keywords"
|
||||
msgstr ""
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1312
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1321
|
||||
msgid "Filter nodes"
|
||||
msgstr ""
|
||||
|
||||
@@ -975,13 +975,13 @@ msgstr ""
|
||||
msgid "Ignore client bandwidth"
|
||||
msgstr ""
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1225
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1238
|
||||
msgid "Import"
|
||||
msgstr ""
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1172
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1251
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1253
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1185
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1264
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1266
|
||||
msgid "Import share links"
|
||||
msgstr ""
|
||||
|
||||
@@ -1317,7 +1317,7 @@ msgstr ""
|
||||
msgid "NOT RUNNING"
|
||||
msgstr ""
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1339
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1348
|
||||
msgid "NOTE: Save current settings before updating subscriptions."
|
||||
msgstr ""
|
||||
|
||||
@@ -1351,15 +1351,15 @@ msgstr ""
|
||||
msgid "No additional encryption support: It's basically duplicate encryption."
|
||||
msgstr ""
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1355
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1364
|
||||
msgid "No subscription available"
|
||||
msgstr ""
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1380
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1389
|
||||
msgid "No subscription node"
|
||||
msgstr ""
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1211
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1224
|
||||
msgid "No valid share link found."
|
||||
msgstr ""
|
||||
|
||||
@@ -1372,7 +1372,7 @@ msgstr ""
|
||||
msgid "Node Settings"
|
||||
msgstr ""
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1161
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1170
|
||||
msgid "Nodes"
|
||||
msgstr ""
|
||||
|
||||
@@ -1700,11 +1700,11 @@ msgstr ""
|
||||
msgid "Remote"
|
||||
msgstr ""
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1377
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1386
|
||||
msgid "Remove %s nodes"
|
||||
msgstr ""
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1367
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1376
|
||||
msgid "Remove all nodes from subscriptions"
|
||||
msgstr ""
|
||||
|
||||
@@ -1803,11 +1803,11 @@ msgstr ""
|
||||
msgid "Same as main node"
|
||||
msgstr ""
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1341
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1350
|
||||
msgid "Save current settings"
|
||||
msgstr ""
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1338
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1347
|
||||
msgid "Save subscriptions settings"
|
||||
msgstr ""
|
||||
|
||||
@@ -1941,19 +1941,19 @@ msgstr ""
|
||||
msgid "String"
|
||||
msgstr ""
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1266
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1275
|
||||
msgid "Sub (%s)"
|
||||
msgstr ""
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1295
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1304
|
||||
msgid "Subscription URL-s"
|
||||
msgstr ""
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1277
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1286
|
||||
msgid "Subscriptions"
|
||||
msgstr ""
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1213
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1226
|
||||
msgid "Successfully imported %s nodes of total %s."
|
||||
msgstr ""
|
||||
|
||||
@@ -1961,8 +1961,8 @@ msgstr ""
|
||||
msgid "Successfully updated."
|
||||
msgstr ""
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1173
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1296
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1186
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1305
|
||||
msgid ""
|
||||
"Support Hysteria, Shadowsocks, Trojan, v2rayN (VMess), and XTLS (VLESS) "
|
||||
"online configuration delivery standard."
|
||||
@@ -2185,7 +2185,7 @@ msgid ""
|
||||
msgstr ""
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:985
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1328
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1337
|
||||
msgid ""
|
||||
"This is <strong>DANGEROUS</strong>, your traffic is almost like "
|
||||
"<strong>PLAIN TEXT</strong>! Use at your own risk!"
|
||||
@@ -2197,6 +2197,11 @@ msgid ""
|
||||
"QUIC stream based UDP relay mode that TUIC does not provide."
|
||||
msgstr ""
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/client.js:167
|
||||
#: htdocs/luci-static/resources/view/homeproxy/client.js:190
|
||||
msgid "ThreatBook Public DNS (117.50.10.10)"
|
||||
msgstr ""
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/client.js:662
|
||||
msgid ""
|
||||
"Timeout of rejected DNS response cache. <code>7d</code> is used by default."
|
||||
@@ -2288,7 +2293,7 @@ msgstr ""
|
||||
msgid "Unsupported fingerprint!"
|
||||
msgstr ""
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1352
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1361
|
||||
msgid "Update %s subscriptions"
|
||||
msgstr ""
|
||||
|
||||
@@ -2304,19 +2309,19 @@ msgstr ""
|
||||
msgid "Update interval of rule set.<br/><code>1d</code> will be used if empty."
|
||||
msgstr ""
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1347
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1356
|
||||
msgid "Update nodes from subscriptions"
|
||||
msgstr ""
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1291
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1300
|
||||
msgid "Update subscriptions via proxy."
|
||||
msgstr ""
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1284
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1293
|
||||
msgid "Update time"
|
||||
msgstr ""
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1290
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1299
|
||||
msgid "Update via proxy"
|
||||
msgstr ""
|
||||
|
||||
@@ -2390,7 +2395,7 @@ msgstr ""
|
||||
msgid "WebSocket"
|
||||
msgstr ""
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1316
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1325
|
||||
msgid "Whitelist mode"
|
||||
msgstr ""
|
||||
|
||||
@@ -2414,13 +2419,8 @@ msgstr ""
|
||||
msgid "Write proxy protocol in the connection header."
|
||||
msgstr ""
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/client.js:167
|
||||
#: htdocs/luci-static/resources/view/homeproxy/client.js:190
|
||||
msgid "Xinfeng Public DNS (114.114.114.114)"
|
||||
msgstr ""
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:828
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1336
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1345
|
||||
msgid "Xudp (Xray-core)"
|
||||
msgstr ""
|
||||
|
||||
@@ -2482,12 +2482,12 @@ msgstr ""
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:567
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:826
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1334
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1343
|
||||
msgid "none"
|
||||
msgstr ""
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:827
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1335
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1344
|
||||
msgid "packet addr (v2ray-core v5+)"
|
||||
msgstr ""
|
||||
|
||||
@@ -2522,7 +2522,7 @@ msgid "unchecked"
|
||||
msgstr ""
|
||||
|
||||
#: htdocs/luci-static/resources/homeproxy.js:221
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1242
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1255
|
||||
msgid "unique UCI identifier"
|
||||
msgstr ""
|
||||
|
||||
@@ -2552,8 +2552,8 @@ msgstr ""
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/client.js:1029
|
||||
#: htdocs/luci-static/resources/view/homeproxy/client.js:1032
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1302
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1305
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1311
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1314
|
||||
msgid "valid URL"
|
||||
msgstr ""
|
||||
|
||||
|
@@ -12,7 +12,7 @@ msgstr ""
|
||||
msgid "%s log"
|
||||
msgstr "%s 日志"
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1399
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1408
|
||||
msgid "%s nodes removed"
|
||||
msgstr "移除了 %s 个节点"
|
||||
|
||||
@@ -106,7 +106,7 @@ msgid "All ports"
|
||||
msgstr "所有端口"
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:982
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1325
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1334
|
||||
msgid "Allow insecure"
|
||||
msgstr "允许不安全连接"
|
||||
|
||||
@@ -114,7 +114,7 @@ msgstr "允许不安全连接"
|
||||
msgid "Allow insecure connection at TLS client."
|
||||
msgstr "允许 TLS 客户端侧的不安全连接。"
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1326
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1335
|
||||
msgid "Allow insecure connection by default when add nodes from subscriptions."
|
||||
msgstr "从订阅获取节点时,默认允许不安全连接。"
|
||||
|
||||
@@ -144,7 +144,7 @@ msgstr "替代 HTTP 端口"
|
||||
msgid "Alternative TLS port"
|
||||
msgstr "替代 HTTPS 端口"
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1362
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1371
|
||||
msgid "An error occurred during updating subscriptions: %s"
|
||||
msgstr "更新订阅时发生错误:%s"
|
||||
|
||||
@@ -202,11 +202,11 @@ msgstr "认证类型"
|
||||
msgid "Auto configure firewall"
|
||||
msgstr "自动配置防火墙"
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1279
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1288
|
||||
msgid "Auto update"
|
||||
msgstr "自动更新"
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1280
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1289
|
||||
msgid "Auto update subscriptions."
|
||||
msgstr "自动更新订阅。"
|
||||
|
||||
@@ -241,7 +241,7 @@ msgid ""
|
||||
"Bind outbound traffic to specific interface. Leave empty to auto detect."
|
||||
msgstr "绑定出站流量至指定端口。留空自动检测。"
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1315
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1324
|
||||
msgid "Blacklist mode"
|
||||
msgstr "黑名单模式"
|
||||
|
||||
@@ -288,7 +288,7 @@ msgstr "CNNIC 公共 DNS(210.2.4.8)"
|
||||
msgid "CUBIC"
|
||||
msgstr "CUBIC"
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1179
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1192
|
||||
msgid "Cancel"
|
||||
msgstr "取消"
|
||||
|
||||
@@ -435,7 +435,7 @@ msgstr "默认域名解析策略。"
|
||||
msgid "Default outbound"
|
||||
msgstr "默认出站"
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1333
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1342
|
||||
msgid "Default packet encoding"
|
||||
msgstr "默认包封装格式"
|
||||
|
||||
@@ -479,7 +479,7 @@ msgstr "直连 MAC 地址"
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:492
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:504
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1061
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1314
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1323
|
||||
#: htdocs/luci-static/resources/view/homeproxy/server.js:246
|
||||
#: htdocs/luci-static/resources/view/homeproxy/server.js:258
|
||||
msgid "Disable"
|
||||
@@ -566,7 +566,7 @@ msgstr "下载带宽"
|
||||
msgid "Download bandwidth in Mbps."
|
||||
msgstr "下载带宽(单位:Mbps)。"
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1321
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1330
|
||||
msgid ""
|
||||
"Drop/keep nodes that contain the specific keywords. <a target=\"_blank\" "
|
||||
"href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/"
|
||||
@@ -576,7 +576,7 @@ msgstr ""
|
||||
"developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Regular_Expressions\">"
|
||||
"正则表达式</a>。"
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1313
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1322
|
||||
msgid "Drop/keep specific nodes from subscriptions."
|
||||
msgstr "从订阅中 丢弃/保留 指定节点"
|
||||
|
||||
@@ -623,7 +623,7 @@ msgstr "前置数据标头"
|
||||
msgid "Early data is sent in path instead of header by default."
|
||||
msgstr "前置数据默认发送在路径而不是标头中。"
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1155
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1164
|
||||
msgid "Edit nodes"
|
||||
msgstr "修改节点"
|
||||
|
||||
@@ -726,9 +726,9 @@ msgstr "加密方式"
|
||||
#: htdocs/luci-static/resources/view/homeproxy/client.js:1200
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:452
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1082
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1242
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1302
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1305
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1255
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1311
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1314
|
||||
#: htdocs/luci-static/resources/view/homeproxy/server.js:211
|
||||
#: htdocs/luci-static/resources/view/homeproxy/server.js:602
|
||||
#: htdocs/luci-static/resources/view/homeproxy/server.js:604
|
||||
@@ -751,11 +751,11 @@ msgstr "外部账户密钥标识符"
|
||||
msgid "Failed to upload %s, error: %s."
|
||||
msgstr "上传 %s 失败,错误:%s。"
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1320
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1329
|
||||
msgid "Filter keywords"
|
||||
msgstr "过滤关键词"
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1312
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1321
|
||||
msgid "Filter nodes"
|
||||
msgstr "过滤节点"
|
||||
|
||||
@@ -999,13 +999,13 @@ msgstr "如果你拥有根证书,使用此选项而不是允许不安全连接
|
||||
msgid "Ignore client bandwidth"
|
||||
msgstr "忽略客户端带宽"
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1225
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1238
|
||||
msgid "Import"
|
||||
msgstr "导入"
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1172
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1251
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1253
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1185
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1264
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1266
|
||||
msgid "Import share links"
|
||||
msgstr "导入分享链接"
|
||||
|
||||
@@ -1345,7 +1345,7 @@ msgstr "多路复用协议。"
|
||||
msgid "NOT RUNNING"
|
||||
msgstr "未运行"
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1339
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1348
|
||||
msgid "NOTE: Save current settings before updating subscriptions."
|
||||
msgstr "注意:更新订阅前先保存当前配置。"
|
||||
|
||||
@@ -1379,15 +1379,15 @@ msgstr "无 TCP 传输层, 纯 HTTP 已合并到 HTTP 传输层。"
|
||||
msgid "No additional encryption support: It's basically duplicate encryption."
|
||||
msgstr "无额外加密支持:它基本上是重复加密。"
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1355
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1364
|
||||
msgid "No subscription available"
|
||||
msgstr "无可用订阅"
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1380
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1389
|
||||
msgid "No subscription node"
|
||||
msgstr "无订阅节点"
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1211
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1224
|
||||
msgid "No valid share link found."
|
||||
msgstr "找不到有效分享链接。"
|
||||
|
||||
@@ -1400,7 +1400,7 @@ msgstr "节点"
|
||||
msgid "Node Settings"
|
||||
msgstr "节点设置"
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1161
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1170
|
||||
msgid "Nodes"
|
||||
msgstr "节点"
|
||||
|
||||
@@ -1728,11 +1728,11 @@ msgstr "区域 ID"
|
||||
msgid "Remote"
|
||||
msgstr "远程"
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1377
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1386
|
||||
msgid "Remove %s nodes"
|
||||
msgstr "移除 %s 个节点"
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1367
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1376
|
||||
msgid "Remove all nodes from subscriptions"
|
||||
msgstr "移除所有订阅节点"
|
||||
|
||||
@@ -1831,11 +1831,11 @@ msgstr "Salamander"
|
||||
msgid "Same as main node"
|
||||
msgstr "保持与主节点一致"
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1341
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1350
|
||||
msgid "Save current settings"
|
||||
msgstr "保存当前设置"
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1338
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1347
|
||||
msgid "Save subscriptions settings"
|
||||
msgstr "保存订阅设置"
|
||||
|
||||
@@ -1980,19 +1980,19 @@ msgstr ""
|
||||
msgid "String"
|
||||
msgstr "字符串"
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1266
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1275
|
||||
msgid "Sub (%s)"
|
||||
msgstr "订阅(%s)"
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1295
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1304
|
||||
msgid "Subscription URL-s"
|
||||
msgstr "订阅地址"
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1277
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1286
|
||||
msgid "Subscriptions"
|
||||
msgstr "订阅"
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1213
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1226
|
||||
msgid "Successfully imported %s nodes of total %s."
|
||||
msgstr "成功导入 %s 个节点,共 %s 个。"
|
||||
|
||||
@@ -2000,8 +2000,8 @@ msgstr "成功导入 %s 个节点,共 %s 个。"
|
||||
msgid "Successfully updated."
|
||||
msgstr "更新成功。"
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1173
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1296
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1186
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1305
|
||||
msgid ""
|
||||
"Support Hysteria, Shadowsocks, Trojan, v2rayN (VMess), and XTLS (VLESS) "
|
||||
"online configuration delivery standard."
|
||||
@@ -2244,7 +2244,7 @@ msgstr ""
|
||||
"检测到任何活动,则会关闭连接。"
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:985
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1328
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1337
|
||||
msgid ""
|
||||
"This is <strong>DANGEROUS</strong>, your traffic is almost like "
|
||||
"<strong>PLAIN TEXT</strong>! Use at your own risk!"
|
||||
@@ -2259,6 +2259,11 @@ msgstr ""
|
||||
"这是 TUIC 的 UDP over TCP 协议移植, 旨在提供 TUIC 不提供的基于 QUIC 流的 "
|
||||
"UDP 中继模式。"
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/client.js:167
|
||||
#: htdocs/luci-static/resources/view/homeproxy/client.js:190
|
||||
msgid "ThreatBook Public DNS (117.50.10.10)"
|
||||
msgstr "微步在线公共 DNS(117.50.10.10)"
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/client.js:662
|
||||
msgid ""
|
||||
"Timeout of rejected DNS response cache. <code>7d</code> is used by default."
|
||||
@@ -2352,7 +2357,7 @@ msgstr "未知错误:%s"
|
||||
msgid "Unsupported fingerprint!"
|
||||
msgstr "不支持的指纹!"
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1352
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1361
|
||||
msgid "Update %s subscriptions"
|
||||
msgstr "更新 %s 个订阅"
|
||||
|
||||
@@ -2368,19 +2373,19 @@ msgstr "更新间隔"
|
||||
msgid "Update interval of rule set.<br/><code>1d</code> will be used if empty."
|
||||
msgstr "规则集更新间隔。<br/>留空使用 <code>1d</code>。"
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1347
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1356
|
||||
msgid "Update nodes from subscriptions"
|
||||
msgstr "从订阅更新节点"
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1291
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1300
|
||||
msgid "Update subscriptions via proxy."
|
||||
msgstr "使用代理更新订阅。"
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1284
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1293
|
||||
msgid "Update time"
|
||||
msgstr "更新时间"
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1290
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1299
|
||||
msgid "Update via proxy"
|
||||
msgstr "使用代理更新"
|
||||
|
||||
@@ -2454,7 +2459,7 @@ msgstr "WAN IP 策略"
|
||||
msgid "WebSocket"
|
||||
msgstr "WebSocket"
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1316
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1325
|
||||
msgid "Whitelist mode"
|
||||
msgstr "白名单模式"
|
||||
|
||||
@@ -2478,13 +2483,8 @@ msgstr "WireGuard 要求 base64 编码的私钥。"
|
||||
msgid "Write proxy protocol in the connection header."
|
||||
msgstr "在连接头中写入代理协议。"
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/client.js:167
|
||||
#: htdocs/luci-static/resources/view/homeproxy/client.js:190
|
||||
msgid "Xinfeng Public DNS (114.114.114.114)"
|
||||
msgstr "信风公共 DNS(114.114.114.114)"
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:828
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1336
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1345
|
||||
msgid "Xudp (Xray-core)"
|
||||
msgstr "Xudp (Xray-core)"
|
||||
|
||||
@@ -2546,12 +2546,12 @@ msgstr "非空值"
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:567
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:826
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1334
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1343
|
||||
msgid "none"
|
||||
msgstr "无"
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:827
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1335
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1344
|
||||
msgid "packet addr (v2ray-core v5+)"
|
||||
msgstr "packet addr (v2ray-core v5+)"
|
||||
|
||||
@@ -2587,7 +2587,7 @@ msgid "unchecked"
|
||||
msgstr "未检查"
|
||||
|
||||
#: htdocs/luci-static/resources/homeproxy.js:221
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1242
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1255
|
||||
msgid "unique UCI identifier"
|
||||
msgstr "独立 UCI 标识"
|
||||
|
||||
@@ -2617,8 +2617,8 @@ msgstr "有效 IP 地址"
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/client.js:1029
|
||||
#: htdocs/luci-static/resources/view/homeproxy/client.js:1032
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1302
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1305
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1311
|
||||
#: htdocs/luci-static/resources/view/homeproxy/node.js:1314
|
||||
msgid "valid URL"
|
||||
msgstr "有效网址"
|
||||
|
||||
|
@@ -88,6 +88,9 @@ const control_info = {};
|
||||
|
||||
for (let i in control_options)
|
||||
control_info[i] = uci.get(cfgname, 'control', i);
|
||||
|
||||
const dns_hijacked = uci.get('dhcp', '@dnsmasq[0]', 'dns_redirect') || '0',
|
||||
dns_port = uci.get('dhcp', '@dnsmasq[0]', 'port') || '53';
|
||||
/* UCI config end */
|
||||
-%}
|
||||
|
||||
@@ -228,6 +231,16 @@ set homeproxy_routing_port {
|
||||
}
|
||||
{% endif %}
|
||||
|
||||
{# DNS hijack & TCP redirect #}
|
||||
chain dstnat {
|
||||
{% if (dns_hijacked !== '1'): %}
|
||||
meta nfproto { ipv4, ipv6 } udp dport 53 counter redirect to :{{ dns_port }} comment "!{{ cfgname }}: DNS hijack"
|
||||
{% endif /* dns_hijacked */ %}
|
||||
{% if (match(proxy_mode, /redirect/)): %}
|
||||
meta nfproto { {{ (ipv6_support === '1') ? 'ipv4, ipv6' : 'ipv4' }} } meta l4proto tcp jump homeproxy_redirect_lanac
|
||||
{% endif /* proxy_mode */ %}
|
||||
}
|
||||
|
||||
{# TCP redirect #}
|
||||
{% if (match(proxy_mode, /redirect/)): %}
|
||||
chain homeproxy_redirect_proxy {
|
||||
@@ -338,10 +351,6 @@ chain homeproxy_output_redir {
|
||||
type nat hook output priority filter -105; policy accept
|
||||
meta nfproto { {{ (ipv6_support === '1') ? 'ipv4, ipv6' : 'ipv4' }} } meta l4proto tcp jump homeproxy_redirect
|
||||
}
|
||||
|
||||
chain dstnat {
|
||||
meta nfproto { {{ (ipv6_support === '1') ? 'ipv4, ipv6' : 'ipv4' }} } meta l4proto tcp jump homeproxy_redirect_lanac
|
||||
}
|
||||
{% endif %}
|
||||
|
||||
{# UDP tproxy #}
|
||||
@@ -371,6 +380,7 @@ chain homeproxy_mangle_lanac {
|
||||
{% if (control_info.listen_interfaces): %}
|
||||
meta iifname != {{ array_to_nftarr(split(join(' ', control_info.listen_interfaces) + ' lo', ' ')) }} counter return
|
||||
{% endif %}
|
||||
meta iifname != io udp dport 53 counter return
|
||||
meta mark {{ self_mark }} counter return
|
||||
|
||||
{% if (control_info.lan_proxy_mode === 'listed_only'): %}
|
||||
@@ -513,6 +523,7 @@ chain mangle_output {
|
||||
{% if (match(proxy_mode, /tun/)): %}
|
||||
chain homeproxy_mangle_lanac {
|
||||
iifname {{ tun_name }} counter return
|
||||
udp dport 53 counter return
|
||||
|
||||
{% if (control_info.listen_interfaces): %}
|
||||
meta iifname != {{ array_to_nftarr(control_info.listen_interfaces) }} counter return
|
||||
|
@@ -30,13 +30,13 @@ define Download/geosite
|
||||
HASH:=8fba86084a952fd635f5a4728c14f7e5eafd225132013163072272859dbbd8cb
|
||||
endef
|
||||
|
||||
GEOSITE_IRAN_VER:=202408260030
|
||||
GEOSITE_IRAN_VER:=202409020032
|
||||
GEOSITE_IRAN_FILE:=iran.dat.$(GEOSITE_IRAN_VER)
|
||||
define Download/geosite-ir
|
||||
URL:=https://github.com/bootmortis/iran-hosted-domains/releases/download/$(GEOSITE_IRAN_VER)/
|
||||
URL_FILE:=iran.dat
|
||||
FILE:=$(GEOSITE_IRAN_FILE)
|
||||
HASH:=d95bd88c33b41514400ced2ec117834dd325c24a46c04e82f8c04ef040648f14
|
||||
HASH:=ec146949ced882d20eb1dd2ada61e00d67bef758bd3772c312e952ccd3693d74
|
||||
endef
|
||||
|
||||
define Package/v2ray-geodata/template
|
||||
|
@@ -1097,7 +1097,7 @@ namespace ServiceLib.Handler.CoreConfig
|
||||
address = Utils.IsNullOrEmpty(dNSItem?.domainDNSAddress) ? Global.DomainDNSAddress.FirstOrDefault() : dNSItem?.domainDNSAddress,
|
||||
domains = [node.address]
|
||||
};
|
||||
servers.AsArray().Insert(0, JsonUtils.SerializeToNode(dnsServer));
|
||||
servers.AsArray().Add(JsonUtils.SerializeToNode(dnsServer));
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
@@ -66,14 +66,12 @@
|
||||
<TextBlock
|
||||
Margin="8,0"
|
||||
VerticalAlignment="Center"
|
||||
IsVisible="False"
|
||||
Text="{x:Static resx:ResUI.TbAutoScrollToEnd}" />
|
||||
<ToggleSwitch
|
||||
x:Name="togScrollToEnd"
|
||||
Margin="8,0"
|
||||
HorizontalAlignment="Left"
|
||||
IsChecked="True"
|
||||
IsVisible="False" />
|
||||
IsChecked="True" />
|
||||
</WrapPanel>
|
||||
<TextBox
|
||||
Name="txtMsg"
|
||||
|
@@ -11,10 +11,11 @@ namespace v2rayN.Desktop.Views
|
||||
public partial class MsgView : UserControl
|
||||
{
|
||||
private static Config? _config;
|
||||
private ConcurrentQueue<string> _queueMsg = new();
|
||||
private int _numMaxMsg = 500;
|
||||
|
||||
private string lastMsgFilter = string.Empty;
|
||||
private bool lastMsgFilterNotAvailable;
|
||||
private ConcurrentBag<string> _lstMsg = [];
|
||||
|
||||
public MsgView()
|
||||
{
|
||||
@@ -74,30 +75,30 @@ namespace v2rayN.Desktop.Views
|
||||
|
||||
if (togScrollToEnd.IsChecked ?? true)
|
||||
{
|
||||
txtMsg.CaretIndex = int.MaxValue;
|
||||
}
|
||||
}
|
||||
|
||||
private void ShowMsg(string msg)
|
||||
{
|
||||
if (_lstMsg.Count > 999)
|
||||
if (_queueMsg.Count > _numMaxMsg)
|
||||
{
|
||||
ClearMsg();
|
||||
for (int k = 0; k < _queueMsg.Count - _numMaxMsg; k++)
|
||||
{
|
||||
_queueMsg.TryDequeue(out _);
|
||||
}
|
||||
}
|
||||
_queueMsg.Enqueue(msg);
|
||||
if (!msg.EndsWith(Environment.NewLine))
|
||||
{
|
||||
_lstMsg.Add(Environment.NewLine);
|
||||
_queueMsg.Enqueue(Environment.NewLine);
|
||||
}
|
||||
_lstMsg.Add(msg);
|
||||
// if (!msg.EndsWith(Environment.NewLine))
|
||||
// {
|
||||
// _lstMsg.Add(Environment.NewLine);
|
||||
// }
|
||||
this.txtMsg.Text = string.Join("", _lstMsg);
|
||||
txtMsg.Text = string.Join("", _queueMsg.ToArray());
|
||||
}
|
||||
|
||||
public void ClearMsg()
|
||||
{
|
||||
_lstMsg.Clear();
|
||||
_queueMsg.Clear();
|
||||
txtMsg.Clear();
|
||||
}
|
||||
|
||||
|
@@ -1,4 +1,5 @@
|
||||
using ReactiveUI;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Reactive.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Windows.Threading;
|
||||
@@ -8,6 +9,8 @@ namespace v2rayN.Views
|
||||
public partial class MsgView
|
||||
{
|
||||
private static Config? _config;
|
||||
private ConcurrentQueue<string> _queueMsg = new();
|
||||
private int _numMaxMsg = 500;
|
||||
|
||||
private string lastMsgFilter = string.Empty;
|
||||
private bool lastMsgFilterNotAvailable;
|
||||
@@ -80,19 +83,24 @@ namespace v2rayN.Views
|
||||
|
||||
private void ShowMsg(string msg)
|
||||
{
|
||||
if (txtMsg.LineCount > 999)
|
||||
if (_queueMsg.Count > _numMaxMsg)
|
||||
{
|
||||
ClearMsg();
|
||||
for (int k = 0; k < _queueMsg.Count - _numMaxMsg; k++)
|
||||
{
|
||||
_queueMsg.TryDequeue(out _);
|
||||
}
|
||||
this.txtMsg.AppendText(msg);
|
||||
}
|
||||
_queueMsg.Enqueue(msg);
|
||||
if (!msg.EndsWith(Environment.NewLine))
|
||||
{
|
||||
this.txtMsg.AppendText(Environment.NewLine);
|
||||
_queueMsg.Enqueue(Environment.NewLine);
|
||||
}
|
||||
txtMsg.Text = string.Join("", _queueMsg.ToArray());
|
||||
}
|
||||
|
||||
public void ClearMsg()
|
||||
{
|
||||
_queueMsg.Clear();
|
||||
txtMsg.Clear();
|
||||
}
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user