diff --git a/go.mod b/go.mod index 96fe15d..1f2430d 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.23.0 require ( github.com/anaskhan96/soup v1.2.5 github.com/bwmarrin/discordgo v0.28.1 - github.com/darklab8/fl-darkstat v1.64.2 + github.com/darklab8/fl-darkstat v1.64.3 github.com/darklab8/go-typelog v0.6.2 github.com/darklab8/go-utils v0.21.1 github.com/pkg/profile v1.7.0 diff --git a/go.sum b/go.sum index 1a4db4c..8a19260 100644 --- a/go.sum +++ b/go.sum @@ -17,6 +17,8 @@ github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46t github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/darklab8/fl-darkstat v1.64.2 h1:Wh6hsi0glY8bzaf1xnUe5IbfpbGZx5B2mfdtNo7ec1w= github.com/darklab8/fl-darkstat v1.64.2/go.mod h1:5QdTzZ16wOAmppHn/Y+4pR+U0pCnKoA9JohuPLRowy8= +github.com/darklab8/fl-darkstat v1.64.3 h1:XRDKgDasMU0RhgBPfd5c5bBWQyVwkZWtUJ7XqKGUy8k= +github.com/darklab8/fl-darkstat v1.64.3/go.mod h1:5QdTzZ16wOAmppHn/Y+4pR+U0pCnKoA9JohuPLRowy8= github.com/darklab8/fl-data-discovery v0.4.0 h1:cUacP+N04/PrYl4cmw687BAaQWVJI32scePjLfmqxEI= github.com/darklab8/fl-data-discovery v0.4.0/go.mod h1:SgRh0N3cWrhslXVkc5LJXK52Mw4+Fdsbmv+0qkNJ6QI= github.com/darklab8/go-typelog v0.6.2 h1:R6urrs4zgtjWfAngB7UclxpkVMA1uW3hEw7PEgSU7pU= diff --git a/vendor/github.com/a-h/templ/.dockerignore b/vendor/github.com/a-h/templ/.dockerignore new file mode 100644 index 0000000..17896fe --- /dev/null +++ b/vendor/github.com/a-h/templ/.dockerignore @@ -0,0 +1,3 @@ +.git +Dockerfile +.dockerignore diff --git a/vendor/github.com/a-h/templ/.gitignore b/vendor/github.com/a-h/templ/.gitignore new file mode 100644 index 0000000..0338eda --- /dev/null +++ b/vendor/github.com/a-h/templ/.gitignore @@ -0,0 +1,28 @@ +# Output. +cmd/templ/templ + +# Logs. +cmd/templ/lspcmd/*log.txt + +# Go code coverage. +coverage.out +coverage + +# Mac filesystem jank. +.DS_Store + +# Docusaurus. +docs/build/ +docs/resources/_gen/ +node_modules/ +dist/ + +# Nix artifacts. +result + +# Editors +## nvim +.null-ls* + +# Go workspace. +go.work diff --git a/vendor/github.com/a-h/templ/.goreleaser.yaml b/vendor/github.com/a-h/templ/.goreleaser.yaml new file mode 100644 index 0000000..456187c --- /dev/null +++ b/vendor/github.com/a-h/templ/.goreleaser.yaml @@ -0,0 +1,72 @@ +builds: + - env: + - CGO_ENABLED=0 + dir: cmd/templ + mod_timestamp: '{{ .CommitTimestamp }}' + flags: + - -trimpath + ldflags: + - -s -w + goos: + - linux + - windows + - darwin + +checksum: + name_template: 'checksums.txt' + +signs: + - id: checksums + cmd: cosign + stdin: '{{ .Env.COSIGN_PASSWORD }}' + output: true + artifacts: checksum + args: + - sign-blob + - --yes + - --key + - env://COSIGN_PRIVATE_KEY + - '--output-certificate=${certificate}' + - '--output-signature=${signature}' + - '${artifact}' + +archives: + - format: tar.gz + name_template: >- + {{ .ProjectName }}_ + {{- title .Os }}_ + {{- if eq .Arch "amd64" }}x86_64 + {{- else if eq .Arch "386" }}i386 + {{- else }}{{ .Arch }}{{ end }} + {{- if .Arm }}v{{ .Arm }}{{ end }} + +kos: + - repository: ghcr.io/a-h/templ + platforms: + - linux/amd64 + - linux/arm64 + tags: + - latest + - '{{.Tag}}' + bare: true + +docker_signs: + - cmd: cosign + artifacts: all + output: true + args: + - sign + - --yes + - --key + - env://COSIGN_PRIVATE_KEY + - '${artifact}' + +snapshot: + name_template: "{{ incpatch .Version }}-next" + +changelog: + sort: asc + filters: + exclude: + - '^docs:' + - '^test:' diff --git a/vendor/github.com/a-h/templ/.ignore b/vendor/github.com/a-h/templ/.ignore new file mode 100644 index 0000000..21cb25e --- /dev/null +++ b/vendor/github.com/a-h/templ/.ignore @@ -0,0 +1,7 @@ +*_templ.go +examples/integration-ct/static/index.js +examples/counter/assets/css/bulma.* +examples/counter/assets/js/htmx.min.js +examples/counter-basic/assets/css/bulma.* +examples/typescript/assets/index.js +package-lock.json diff --git a/vendor/github.com/a-h/templ/.version b/vendor/github.com/a-h/templ/.version new file mode 100644 index 0000000..baee64f --- /dev/null +++ b/vendor/github.com/a-h/templ/.version @@ -0,0 +1 @@ +0.2.747 \ No newline at end of file diff --git a/vendor/github.com/a-h/templ/CODE_OF_CONDUCT.md b/vendor/github.com/a-h/templ/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..08340d3 --- /dev/null +++ b/vendor/github.com/a-h/templ/CODE_OF_CONDUCT.md @@ -0,0 +1,128 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, religion, or sexual identity +and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the + overall community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or + advances of any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email + address, without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +adrianhesketh@hushail.com. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series +of actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or +permanent ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within +the community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.0, available at +https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. + +Community Impact Guidelines were inspired by [Mozilla's code of conduct +enforcement ladder](https://github.com/mozilla/diversity). + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see the FAQ at +https://www.contributor-covenant.org/faq. Translations are available at +https://www.contributor-covenant.org/translations. diff --git a/vendor/github.com/a-h/templ/CONTRIBUTING.md b/vendor/github.com/a-h/templ/CONTRIBUTING.md new file mode 100644 index 0000000..e98d31f --- /dev/null +++ b/vendor/github.com/a-h/templ/CONTRIBUTING.md @@ -0,0 +1,244 @@ +# Contributing to templ + +## Vision + +Enable Go developers to build strongly typed, component-based HTML user interfaces with first-class developer tooling, and a short learning curve. + +## Come up with a design and share it + +Before starting work on any major pull requests or code changes, start a discussion at https://github.com/a-h/templ/discussions or raise an issue. + +We don't want you to spend time on a PR or feature that ultimately doesn't get merged because it doesn't fit with the project goals, or the design doesn't work for some reason. + +For issues, it really helps if you provide a reproduction repo, or can create a failing unit test to describe the behaviour. + +In designs, we need to consider: + +* Backwards compatibility - Not changing the public API between releases, introducing gradual deprecation - don't break people's code. +* Correctness over time - How can we reduce the risk of defects both now, and in future releases? +* Threat model - How could each change be used to inject vulnerabilities into web pages? +* Go version - We target the oldest supported version of Go as per https://go.dev/doc/devel/release +* Automatic migration - If we need to force through a change. +* Compile time vs runtime errors - Prefer compile time. +* Documentation - New features are only useful if people can understand the new feature, what would the documentation look like? +* Examples - How will we demonstrate the feature? + +## Project structure + +templ is structured into a few areas: + +### Parser `./parser` + +The parser directory currently contains both v1 and v2 parsers. + +The v1 parser is not maintained, it's only used to migrate v1 code over to the v2 syntax. + +The parser is responsible for parsing templ files into an object model. The types that make up the object model are in `types.go`. Automatic formatting of the types is tested in `types_test.go`. + +A templ file is parsed into the `TemplateFile` struct object model. + +```go +type TemplateFile struct { + // Header contains comments or whitespace at the top of the file. + Header []GoExpression + // Package expression. + Package Package + // Nodes in the file. + Nodes []TemplateFileNode +} +``` + +Parsers are individually tested using two types of unit test. + +One test covers the successful parsing of text into an object. For example, the `HTMLCommentParser` test checks for successful patterns. + +```go +func TestHTMLCommentParser(t *testing.T) { + var tests = []struct { + name string + input string + expected HTMLComment + }{ + { + name: "comment - single line", + input: ``, + expected: HTMLComment{ + Contents: " single line comment ", + }, + }, + { + name: "comment - no whitespace", + input: ``, + expected: HTMLComment{ + Contents: "no whitespace between sequence open and close", + }, + }, + { + name: "comment - multiline", + input: ``, + expected: HTMLComment{ + Contents: ` multiline + comment + `, + }, + }, + { + name: "comment - with tag", + input: ``, + expected: HTMLComment{ + Contents: `

tag

`, + }, + }, + { + name: "comments can contain tags", + input: ``, + expected: HTMLComment{ + Contents: `
hello world
`, + }, + }, + } + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + input := parse.NewInput(tt.input) + result, ok, err := htmlComment.Parse(input) + if err != nil { + t.Fatalf("parser error: %v", err) + } + if !ok { + t.Fatalf("failed to parse at %d", input.Index()) + } + if diff := cmp.Diff(tt.expected, result); diff != "" { + t.Errorf(diff) + } + }) + } +} +``` + +Alongside each success test, is a similar test to check that invalid syntax is detected. + +```go +func TestHTMLCommentParserErrors(t *testing.T) { + var tests = []struct { + name string + input string + expected error + }{ + { + name: "unclosed HTML comment", + input: `' not found", + parse.Position{ + Index: 26, + Line: 0, + Col: 26, + }), + }, + { + name: "comment in comment", + input: ` -->`, + expected: parse.Error("comment contains invalid sequence '--'", parse.Position{ + Index: 8, + Line: 0, + Col: 8, + }), + }, + } + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + input := parse.NewInput(tt.input) + _, _, err := htmlComment.Parse(input) + if diff := cmp.Diff(tt.expected, err); diff != "" { + t.Error(diff) + } + }) + } +} +``` + +### Generator + +The generator takes the object model and writes out Go code that produces the expected output. Any changes to Go code output by templ are made in this area. + +Testing of the generator is carried out by creating a templ file, and a matching expected output file. + +For example, `./generator/test-a-href` contains a templ file of: + +```templ +package testahref + +templ render() { + Ignored + Sanitized + Unsanitized +} +``` + +It also contains an expected output file. + +```html +Ignored +Sanitized +Unsanitized +``` + +These tests contribute towards the code coverage metrics by building an instrumented test CLI program. See the `test-cover` task in the `README.md` file. + +### CLI + +The command line interface for templ is used to generate Go code from templ files, format templ files, and run the LSP. + +The code for this is at `./cmd/templ`. + +Testing of the templ command line is done with unit tests to check the argument parsing. + +The `templ generate` command is tested by generating templ files in the project, and testing that the expected output HTML is present. + +### Runtime + +The runtime is used by generated code, and by template authors, to serve template content over HTTP, and to carry out various operations. + +It is in the root directory of the project at `./runtime.go`. The runtime is unit tested, as well as being tested as part of the `generate` tests. + +### LSP + +The LSP is structured within the command line interface, and proxies commands through to the `gopls` LSP. + +### Docs + +The docs are a Docusaurus project at `./docs`. + +## Coding + +### Build tasks + +templ uses the `xc` task runner - https://github.com/joerdav/xc + +If you run `xc` you can get see a list of the development tasks that can be run, or you can read the `README.md` file and see the `Tasks` section. + +The most useful tasks for local development are: + +* `install-snapshot` - this builds the templ CLI and installs it into `~/bin`. Ensure that this is in your path. +* `test` - this regenerates all templates, and runs the unit tests. +* `fmt` - run the `gofmt` tool to format all Go code. +* `lint` - run the same linting as run in the CI process. +* `docs-run` - run the Docusaurus documentation site. + +### Commit messages + +The project using https://www.conventionalcommits.org/en/v1.0.0/ + +Examples: + +* `feat: support Go comments in templates, fixes #234"` + +### Coding style + +* Reduce nesting - i.e. prefer early returns over an `else` block, as per https://danp.net/posts/reducing-go-nesting/ or https://go.dev/doc/effective_go#if +* Use line breaks to separate "paragraphs" of code - don't use line breaks in between lines, or at the start/end of functions etc. +* Use the `fmt` and `lint` build tasks to format and lint your code before submitting a PR. + diff --git a/vendor/github.com/a-h/templ/LICENSE b/vendor/github.com/a-h/templ/LICENSE new file mode 100644 index 0000000..15e6fb8 --- /dev/null +++ b/vendor/github.com/a-h/templ/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 Adrian Hesketh + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/a-h/templ/README.md b/vendor/github.com/a-h/templ/README.md new file mode 100644 index 0000000..e3087f0 --- /dev/null +++ b/vendor/github.com/a-h/templ/README.md @@ -0,0 +1,171 @@ +![templ](https://github.com/a-h/templ/raw/main/templ.png) + +## An HTML templating language for Go that has great developer tooling. + +![templ](ide-demo.gif) + + +## Documentation + +See user documentation at https://templ.guide + +

+Go Reference +xc compatible +Go Coverage +Go Report Card + +## Tasks + +### build + +Build a local version. + +```sh +go run ./get-version > .version +cd cmd/templ +go build +``` + +### nix-update-gomod2nix + +```sh +gomod2nix +``` + +### install-snapshot + +Build and install current version. + +```sh +# Remove templ from the non-standard ~/bin/templ path +# that this command previously used. +rm -f ~/bin/templ +# Clear LSP logs. +rm -f cmd/templ/lspcmd/*.txt +# Update version. +go run ./get-version > .version +# Install to $GOPATH/bin or $HOME/go/bin +cd cmd/templ && go install +``` + +### build-snapshot + +Use goreleaser to build the command line binary using goreleaser. + +```sh +goreleaser build --snapshot --clean +``` + +### generate + +Run templ generate using local version. + +```sh +go run ./cmd/templ generate -include-version=false +``` + +### test + +Run Go tests. + +```sh +go run ./get-version > .version +go run ./cmd/templ generate -include-version=false +go test ./... +``` + +### test-short + +Run Go tests. + +```sh +go run ./get-version > .version +go run ./cmd/templ generate -include-version=false +go test ./... -short +``` + +### test-cover + +Run Go tests. + +```sh +# Create test profile directories. +mkdir -p coverage/fmt +mkdir -p coverage/generate +mkdir -p coverage/version +mkdir -p coverage/unit +# Build the test binary. +go build -cover -o ./coverage/templ-cover ./cmd/templ +# Run the covered generate command. +GOCOVERDIR=coverage/fmt ./coverage/templ-cover fmt . +GOCOVERDIR=coverage/generate ./coverage/templ-cover generate -include-version=false +GOCOVERDIR=coverage/version ./coverage/templ-cover version +# Run the unit tests. +go test -cover ./... -coverpkg ./... -args -test.gocoverdir="$PWD/coverage/unit" +# Display the combined percentage. +go tool covdata percent -i=./coverage/fmt,./coverage/generate,./coverage/version,./coverage/unit +# Generate a text coverage profile for tooling to use. +go tool covdata textfmt -i=./coverage/fmt,./coverage/generate,./coverage/version,./coverage/unit -o coverage.out +# Print total +go tool cover -func coverage.out | grep total +``` + +### test-cover-watch + +```sh +gotestsum --watch -- -coverprofile=coverage.out +``` + +### benchmark + +Run benchmarks. + +```sh +go run ./cmd/templ generate -include-version=false && go test ./... -bench=. -benchmem +``` + +### fmt + +Format all Go and templ code. + +```sh +gofmt -s -w . +go run ./cmd/templ fmt . +``` + +### lint + +```sh +golangci-lint run --verbose +``` + +### push-release-tag + +Push a semantic version number to Github to trigger the release process. + +```sh +./push-tag.sh +``` + +### docs-run + +Run the development server. + +Directory: docs + +```sh +npm run start +``` + +### docs-build + +Build production docs site. + +Directory: docs + +```sh +npm run build +``` + diff --git a/vendor/github.com/a-h/templ/SECURITY.md b/vendor/github.com/a-h/templ/SECURITY.md new file mode 100644 index 0000000..8241f55 --- /dev/null +++ b/vendor/github.com/a-h/templ/SECURITY.md @@ -0,0 +1,9 @@ +# Security Policy + +## Supported Versions + +The latest version of templ is supported. + +## Reporting a Vulnerability + +Use the "Security" tab in Github and fill out the "Report a vulnerability" form. diff --git a/vendor/github.com/a-h/templ/cosign.pub b/vendor/github.com/a-h/templ/cosign.pub new file mode 100644 index 0000000..9d7967b --- /dev/null +++ b/vendor/github.com/a-h/templ/cosign.pub @@ -0,0 +1,4 @@ +-----BEGIN PUBLIC KEY----- +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEqHp75uAj8XqKrLO2YvY0M2EddckH +evQnNAj+0GmBptqdf3NJcUCjL6w4z2Ikh/Zb8lh6b13akAwO/dJQaMLoMA== +-----END PUBLIC KEY----- diff --git a/vendor/github.com/a-h/templ/flake.lock b/vendor/github.com/a-h/templ/flake.lock new file mode 100644 index 0000000..af4e370 --- /dev/null +++ b/vendor/github.com/a-h/templ/flake.lock @@ -0,0 +1,140 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1694529238, + "narHash": "sha256-zsNZZGTGnMOf9YpHKJqMSsa0dXbfmxeoJ7xHlrt+xmY=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "ff7b65b44d01cf9ba6a71320833626af21126384", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "flake-utils_2": { + "locked": { + "lastModified": 1667395993, + "narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "gitignore": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1709087332, + "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", + "owner": "hercules-ci", + "repo": "gitignore.nix", + "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "gitignore.nix", + "type": "github" + } + }, + "gomod2nix": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1717050755, + "narHash": "sha256-C9IEHABulv2zEDFA+Bf0E1nmfN4y6MIUe5eM2RCrDC0=", + "owner": "nix-community", + "repo": "gomod2nix", + "rev": "31b6d2e40b36456e792cd6cf50d5a8ddd2fa59a1", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "gomod2nix", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1720096762, + "narHash": "sha256-KvpJIWxTNuaSpN2L/9TmTlEhlwxEnzJ1vCpEcfK/4mQ=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "638369f687471823770f6d3093f1721dc7b8c897", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "release-24.05", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "gitignore": "gitignore", + "gomod2nix": "gomod2nix", + "nixpkgs": "nixpkgs", + "xc": "xc" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "xc": { + "inputs": { + "flake-utils": "flake-utils_2", + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1717601811, + "narHash": "sha256-+XQvDRXpzjBdZI3JGKP6SAOYXM+JSEbWL5kqtCwRJXE=", + "owner": "joerdav", + "repo": "xc", + "rev": "f8e8e658978d6c9fe49c27b684ca7375a74deef1", + "type": "github" + }, + "original": { + "owner": "joerdav", + "repo": "xc", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/vendor/github.com/a-h/templ/flake.nix b/vendor/github.com/a-h/templ/flake.nix new file mode 100644 index 0000000..fd8a238 --- /dev/null +++ b/vendor/github.com/a-h/templ/flake.nix @@ -0,0 +1,93 @@ +{ + description = "templ"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/release-24.05"; + gomod2nix = { + url = "github:nix-community/gomod2nix"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + gitignore = { + url = "github:hercules-ci/gitignore.nix"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + xc = { + url = "github:joerdav/xc"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + }; + + outputs = { self, nixpkgs, gomod2nix, gitignore, xc }: + let + allSystems = [ + "x86_64-linux" # 64-bit Intel/AMD Linux + "aarch64-linux" # 64-bit ARM Linux + "x86_64-darwin" # 64-bit Intel macOS + "aarch64-darwin" # 64-bit ARM macOS + ]; + forAllSystems = f: nixpkgs.lib.genAttrs allSystems (system: f { + inherit system; + pkgs = import nixpkgs { inherit system; }; + }); + in + { + packages = forAllSystems ({ system, pkgs, ... }: + let + buildGoApplication = gomod2nix.legacyPackages.${system}.buildGoApplication; + in + rec { + default = templ; + + templ = buildGoApplication { + name = "templ"; + src = gitignore.lib.gitignoreSource ./.; + # Update to latest Go version when https://nixpk.gs/pr-tracker.html?pr=324123 is backported to release-24.05. + go = pkgs.go; + # Must be added due to bug https://github.com/nix-community/gomod2nix/issues/120 + pwd = ./.; + subPackages = [ "cmd/templ" ]; + CGO_ENABLED = 0; + flags = [ + "-trimpath" + ]; + ldflags = [ + "-s" + "-w" + "-extldflags -static" + ]; + }; + }); + + # `nix develop` provides a shell containing development tools. + devShell = forAllSystems ({ system, pkgs }: + pkgs.mkShell { + buildInputs = with pkgs; [ + (golangci-lint.override { buildGoModule = buildGo121Module; }) + cosign # Used to sign container images. + esbuild # Used to package JS examples. + go_1_21 + gomod2nix.legacyPackages.${system}.gomod2nix + gopls + goreleaser + gotestsum + ko # Used to build Docker images. + nodejs # Used to build templ-docs. + xc.packages.${system}.xc + ]; + }); + + # This flake outputs an overlay that can be used to add templ and + # templ-docs to nixpkgs as per https://templ.guide/quick-start/installation/#nix + # + # Example usage: + # + # nixpkgs.overlays = [ + # inputs.templ.overlays.default + # ]; + overlays.default = final: prev: { + templ = self.packages.${final.stdenv.system}.templ; + templ-docs = self.packages.${final.stdenv.system}.templ-docs; + }; + }; +} + diff --git a/vendor/github.com/a-h/templ/flush.go b/vendor/github.com/a-h/templ/flush.go new file mode 100644 index 0000000..56d7d3a --- /dev/null +++ b/vendor/github.com/a-h/templ/flush.go @@ -0,0 +1,36 @@ +package templ + +import ( + "context" + "io" +) + +// Flush flushes the output buffer after all its child components have been rendered. +func Flush() FlushComponent { + return FlushComponent{} +} + +type FlushComponent struct { +} + +type flusherError interface { + Flush() error +} + +type flusher interface { + Flush() +} + +func (f FlushComponent) Render(ctx context.Context, w io.Writer) (err error) { + if err = GetChildren(ctx).Render(ctx, w); err != nil { + return err + } + switch w := w.(type) { + case flusher: + w.Flush() + return nil + case flusherError: + return w.Flush() + } + return nil +} diff --git a/vendor/github.com/a-h/templ/gomod2nix.toml b/vendor/github.com/a-h/templ/gomod2nix.toml new file mode 100644 index 0000000..d572a5f --- /dev/null +++ b/vendor/github.com/a-h/templ/gomod2nix.toml @@ -0,0 +1,90 @@ +schema = 3 + +[mod] + [mod."github.com/PuerkitoBio/goquery"] + version = "v1.8.1" + hash = "sha256-z2RaB8PVPEzSJdMUfkfNjT616yXWTjW2gkhNOh989ZU=" + [mod."github.com/a-h/htmlformat"] + version = "v0.0.0-20231108124658-5bd994fe268e" + hash = "sha256-YSl9GsXhc0L2oKGZLwwjUtpe5W6ra6kk74zvQdsDCMU=" + [mod."github.com/a-h/parse"] + version = "v0.0.0-20240121214402-3caf7543159a" + hash = "sha256-ee/g6xwwhtF7vVt3griUSh96Kz4z0hM5/tpXxHW6PZk=" + [mod."github.com/a-h/pathvars"] + version = "v0.0.14" + hash = "sha256-2NytUpcO0zbzE5XunCLcK3jDqxYzmyb3WqtYDEudAYg=" + [mod."github.com/a-h/protocol"] + version = "v0.0.0-20240704131721-1e461c188041" + hash = "sha256-KSw8m+kVIubEi+nuS3dMdBw2ZZTlmcKD/hGbVRFaE5Q=" + [mod."github.com/andybalholm/brotli"] + version = "v1.1.0" + hash = "sha256-njLViV4v++ZdgOWGWzlvkefuFvA/nkugl3Ta/h1nu/0=" + [mod."github.com/andybalholm/cascadia"] + version = "v1.3.1" + hash = "sha256-M0u22DXSeXUaYtl1KoW1qWL46niFpycFkraCEQ/luYA=" + [mod."github.com/cenkalti/backoff/v4"] + version = "v4.3.0" + hash = "sha256-wfVjNZsGG1WoNC5aL+kdcy6QXPgZo4THAevZ1787md8=" + [mod."github.com/cli/browser"] + version = "v1.3.0" + hash = "sha256-06hcvQeOEm31clxkTuZ8ts8ZtdNKY575EsM1osRVpLg=" + [mod."github.com/fatih/color"] + version = "v1.16.0" + hash = "sha256-Aq/SM28aPJVzvapllQ64R/DM4aZ5CHPewcm/AUJPyJQ=" + [mod."github.com/fsnotify/fsnotify"] + version = "v1.7.0" + hash = "sha256-MdT2rQyQHspPJcx6n9ozkLbsktIOJutOqDuKpNAtoZY=" + [mod."github.com/google/go-cmp"] + version = "v0.6.0" + hash = "sha256-qgra5jze4iPGP0JSTVeY5qV5AvEnEu39LYAuUCIkMtg=" + [mod."github.com/mattn/go-colorable"] + version = "v0.1.13" + hash = "sha256-qb3Qbo0CELGRIzvw7NVM1g/aayaz4Tguppk9MD2/OI8=" + [mod."github.com/mattn/go-isatty"] + version = "v0.0.20" + hash = "sha256-qhw9hWtU5wnyFyuMbKx+7RB8ckQaFQ8D+8GKPkN3HHQ=" + [mod."github.com/natefinch/atomic"] + version = "v1.0.1" + hash = "sha256-fbOVHCwRNI8PFjC4o0YXpKZO0JU2aWTfH5c7WXXKMHg=" + [mod."github.com/rs/cors"] + version = "v1.11.0" + hash = "sha256-hF25bVehtWCQsxiOfLuL4Hv8NKVunEqLPk/Vcuheha0=" + [mod."github.com/segmentio/asm"] + version = "v1.2.0" + hash = "sha256-zbNuKxNrUDUc6IlmRQNuJQzVe5Ol/mqp7srDg9IMMqs=" + [mod."github.com/segmentio/encoding"] + version = "v0.4.0" + hash = "sha256-4pWI9eTZRRDP9kO8rG6vbLCtBVVRLtbCJKd0Z2+8JoU=" + [mod."github.com/stretchr/testify"] + version = "v1.8.4" + hash = "sha256-MoOmRzbz9QgiJ+OOBo5h5/LbilhJfRUryvzHJmXAWjo=" + [mod."go.lsp.dev/jsonrpc2"] + version = "v0.10.0" + hash = "sha256-RbRsMYVBLR7ZDHHGMooycrkdbIauMXkQjVOGP7ggSgM=" + [mod."go.lsp.dev/pkg"] + version = "v0.0.0-20210717090340-384b27a52fb2" + hash = "sha256-TxS0Iqe1wbIaFe7MWZJRQdgqhKE8i8CggaGSV9zU1Vg=" + [mod."go.lsp.dev/uri"] + version = "v0.3.0" + hash = "sha256-jGP0N7Gf+bql5oJraUo33sXqWg7AKOTj0D8b4paV4dc=" + [mod."go.uber.org/multierr"] + version = "v1.11.0" + hash = "sha256-Lb6rHHfR62Ozg2j2JZy3MKOMKdsfzd1IYTR57r3Mhp0=" + [mod."go.uber.org/zap"] + version = "v1.27.0" + hash = "sha256-8655KDrulc4Das3VRduO9MjCn8ZYD5WkULjCvruaYsU=" + [mod."golang.org/x/mod"] + version = "v0.17.0" + hash = "sha256-CLaPeF6uTFuRDv4oHwOQE6MCMvrzkUjWN3NuyywZjKU=" + [mod."golang.org/x/net"] + version = "v0.24.0" + hash = "sha256-w1c21ljta5wNIyel9CSIn/crPzwOCRofNKhqmfs4aEQ=" + [mod."golang.org/x/sync"] + version = "v0.3.0" + hash = "sha256-bCJKLvwExhYacH2ZrWlZ38lr1d6oNenNt2m1QqDCs0o=" + [mod."golang.org/x/sys"] + version = "v0.21.0" + hash = "sha256-gapzPWuEqY36V6W2YhIDYR49sEvjJRd7bSuf9K1f4JY=" + [mod."golang.org/x/tools"] + version = "v0.13.0" + hash = "sha256-OCgLOwia8fNHxfdogXVApf0/qK6jE2ukegOx7lkOzfo=" diff --git a/vendor/github.com/a-h/templ/handler.go b/vendor/github.com/a-h/templ/handler.go new file mode 100644 index 0000000..a28d561 --- /dev/null +++ b/vendor/github.com/a-h/templ/handler.go @@ -0,0 +1,102 @@ +package templ + +import "net/http" + +// ComponentHandler is a http.Handler that renders components. +type ComponentHandler struct { + Component Component + Status int + ContentType string + ErrorHandler func(r *http.Request, err error) http.Handler + StreamResponse bool +} + +const componentHandlerErrorMessage = "templ: failed to render template" + +func (ch *ComponentHandler) ServeHTTPBuffered(w http.ResponseWriter, r *http.Request) { + // Since the component may error, write to a buffer first. + // This prevents partial responses from being written to the client. + buf := GetBuffer() + defer ReleaseBuffer(buf) + err := ch.Component.Render(r.Context(), buf) + if err != nil { + if ch.ErrorHandler != nil { + w.Header().Set("Content-Type", ch.ContentType) + ch.ErrorHandler(r, err).ServeHTTP(w, r) + return + } + http.Error(w, componentHandlerErrorMessage, http.StatusInternalServerError) + return + } + w.Header().Set("Content-Type", ch.ContentType) + if ch.Status != 0 { + w.WriteHeader(ch.Status) + } + // Ignore write error like http.Error() does, because there is + // no way to recover at this point. + _, _ = w.Write(buf.Bytes()) +} + +func (ch *ComponentHandler) ServeHTTPStreamed(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", ch.ContentType) + if ch.Status != 0 { + w.WriteHeader(ch.Status) + } + if err := ch.Component.Render(r.Context(), w); err != nil { + if ch.ErrorHandler != nil { + w.Header().Set("Content-Type", ch.ContentType) + ch.ErrorHandler(r, err).ServeHTTP(w, r) + return + } + http.Error(w, componentHandlerErrorMessage, http.StatusInternalServerError) + } +} + +// ServeHTTP implements the http.Handler interface. +func (ch ComponentHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + if ch.StreamResponse { + ch.ServeHTTPStreamed(w, r) + return + } + ch.ServeHTTPBuffered(w, r) +} + +// Handler creates a http.Handler that renders the template. +func Handler(c Component, options ...func(*ComponentHandler)) *ComponentHandler { + ch := &ComponentHandler{ + Component: c, + ContentType: "text/html; charset=utf-8", + } + for _, o := range options { + o(ch) + } + return ch +} + +// WithStatus sets the HTTP status code returned by the ComponentHandler. +func WithStatus(status int) func(*ComponentHandler) { + return func(ch *ComponentHandler) { + ch.Status = status + } +} + +// WithContentType sets the Content-Type header returned by the ComponentHandler. +func WithContentType(contentType string) func(*ComponentHandler) { + return func(ch *ComponentHandler) { + ch.ContentType = contentType + } +} + +// WithErrorHandler sets the error handler used if rendering fails. +func WithErrorHandler(eh func(r *http.Request, err error) http.Handler) func(*ComponentHandler) { + return func(ch *ComponentHandler) { + ch.ErrorHandler = eh + } +} + +// WithStreaming sets the ComponentHandler to stream the response instead of buffering it. +func WithStreaming() func(*ComponentHandler) { + return func(ch *ComponentHandler) { + ch.StreamResponse = true + } +} diff --git a/vendor/github.com/a-h/templ/ide-demo.gif b/vendor/github.com/a-h/templ/ide-demo.gif new file mode 100644 index 0000000..e35fd68 Binary files /dev/null and b/vendor/github.com/a-h/templ/ide-demo.gif differ diff --git a/vendor/github.com/a-h/templ/jsonscript.go b/vendor/github.com/a-h/templ/jsonscript.go new file mode 100644 index 0000000..6e88174 --- /dev/null +++ b/vendor/github.com/a-h/templ/jsonscript.go @@ -0,0 +1,85 @@ +package templ + +import ( + "context" + "encoding/json" + "fmt" + "io" +) + +var _ Component = JSONScriptElement{} + +// JSONScript renders a JSON object inside a script element. +// e.g. +func JSONScript(id string, data any) JSONScriptElement { + return JSONScriptElement{ + ID: id, + Type: "application/json", + Data: data, + Nonce: GetNonce, + } +} + +// WithType sets the value of the type attribute of the script element. +func (j JSONScriptElement) WithType(t string) JSONScriptElement { + j.Type = t + return j +} + +// WithNonceFromString sets the value of the nonce attribute of the script element to the given string. +func (j JSONScriptElement) WithNonceFromString(nonce string) JSONScriptElement { + j.Nonce = func(context.Context) string { + return nonce + } + return j +} + +// WithNonceFrom sets the value of the nonce attribute of the script element to the value returned by the given function. +func (j JSONScriptElement) WithNonceFrom(f func(context.Context) string) JSONScriptElement { + j.Nonce = f + return j +} + +type JSONScriptElement struct { + // ID of the element in the DOM. + ID string + // Type of the script element, defaults to "application/json". + Type string + // Data that will be encoded as JSON. + Data any + // Nonce is a function that returns a CSP nonce. + // Defaults to CSPNonceFromContext. + // See https://content-security-policy.com/nonce for more information. + Nonce func(ctx context.Context) string +} + +func (j JSONScriptElement) Render(ctx context.Context, w io.Writer) (err error) { + if _, err = io.WriteString(w, ""); err != nil { + return err + } + if err = json.NewEncoder(w).Encode(j.Data); err != nil { + return err + } + if _, err = io.WriteString(w, ""); err != nil { + return err + } + return nil +} diff --git a/vendor/github.com/a-h/templ/jsonstring.go b/vendor/github.com/a-h/templ/jsonstring.go new file mode 100644 index 0000000..425e4e8 --- /dev/null +++ b/vendor/github.com/a-h/templ/jsonstring.go @@ -0,0 +1,14 @@ +package templ + +import ( + "encoding/json" +) + +// JSONString returns a JSON encoded string of v. +func JSONString(v any) (string, error) { + b, err := json.Marshal(v) + if err != nil { + return "", err + } + return string(b), nil +} diff --git a/vendor/github.com/a-h/templ/once.go b/vendor/github.com/a-h/templ/once.go new file mode 100644 index 0000000..7860ab8 --- /dev/null +++ b/vendor/github.com/a-h/templ/once.go @@ -0,0 +1,64 @@ +package templ + +import ( + "context" + "io" + "sync/atomic" +) + +// onceHandleIndex is used to identify unique once handles in a program run. +var onceHandleIndex int64 + +type OnceOpt func(*OnceHandle) + +// WithOnceComponent sets the component to be rendered once per context. +// This can be used instead of setting the children of the `Once` method, +// for example, if creating a code component outside of a templ HTML template. +func WithComponent(c Component) OnceOpt { + return func(o *OnceHandle) { + o.c = c + } +} + +// NewOnceHandle creates a OnceHandle used to ensure that the children of its +// `Once` method are only rendered once per context. +func NewOnceHandle(opts ...OnceOpt) *OnceHandle { + oh := &OnceHandle{ + id: atomic.AddInt64(&onceHandleIndex, 1), + } + for _, opt := range opts { + opt(oh) + } + return oh +} + +// OnceHandle is used to ensure that the children of its `Once` method are are only +// rendered once per context. +type OnceHandle struct { + // id is used to identify which instance of the OnceHandle is being used. + // The OnceHandle can't be an empty struct, because: + // + // | Two distinct zero-size variables may + // | have the same address in memory + // + // https://go.dev/ref/spec#Size_and_alignment_guarantees + id int64 + // c is the component to be rendered once per context. + // if c is nil, the children of the `Once` method are rendered. + c Component +} + +// Once returns a component that renders its children once per context. +func (o *OnceHandle) Once() Component { + return ComponentFunc(func(ctx context.Context, w io.Writer) (err error) { + _, v := getContext(ctx) + if v.getHasBeenRendered(o) { + return nil + } + v.setHasBeenRendered(o) + if o.c != nil { + return o.c.Render(ctx, w) + } + return GetChildren(ctx).Render(ctx, w) + }) +} diff --git a/vendor/github.com/a-h/templ/push-tag.sh b/vendor/github.com/a-h/templ/push-tag.sh new file mode 100644 index 0000000..9eedeed --- /dev/null +++ b/vendor/github.com/a-h/templ/push-tag.sh @@ -0,0 +1,14 @@ +#!/bin/sh +if [ `git rev-parse --abbrev-ref HEAD` != "main" ]; then + echo "Error: Not on main branch. Please switch to main branch."; + exit 1; +fi +git pull +if ! git diff --quiet; then + echo "Error: Working directory is not clean. Please commit the changes first."; + exit 1; +fi +export VERSION=`cat .version` +echo Adding git tag with version v${VERSION}; +git tag v${VERSION}; +git push origin v${VERSION}; diff --git a/vendor/github.com/a-h/templ/runtime.go b/vendor/github.com/a-h/templ/runtime.go new file mode 100644 index 0000000..d4d5aa0 --- /dev/null +++ b/vendor/github.com/a-h/templ/runtime.go @@ -0,0 +1,855 @@ +package templ + +import ( + "bytes" + "context" + "crypto/sha256" + "encoding/hex" + "encoding/json" + "errors" + "fmt" + "html" + "html/template" + "io" + "net/http" + "os" + "reflect" + "runtime" + "sort" + "strconv" + "strings" + "sync" + "time" + + "github.com/a-h/templ/safehtml" +) + +// Types exposed by all components. + +// Component is the interface that all templates implement. +type Component interface { + // Render the template. + Render(ctx context.Context, w io.Writer) error +} + +// ComponentFunc converts a function that matches the Component interface's +// Render method into a Component. +type ComponentFunc func(ctx context.Context, w io.Writer) error + +// Render the template. +func (cf ComponentFunc) Render(ctx context.Context, w io.Writer) error { + return cf(ctx, w) +} + +// WithNonce sets a CSP nonce on the context and returns it. +func WithNonce(ctx context.Context, nonce string) context.Context { + ctx, v := getContext(ctx) + v.nonce = nonce + return ctx +} + +// GetNonce returns the CSP nonce value set with WithNonce, or an +// empty string if none has been set. +func GetNonce(ctx context.Context) (nonce string) { + if ctx == nil { + return "" + } + _, v := getContext(ctx) + return v.nonce +} + +func WithChildren(ctx context.Context, children Component) context.Context { + ctx, v := getContext(ctx) + v.children = &children + return ctx +} + +func ClearChildren(ctx context.Context) context.Context { + _, v := getContext(ctx) + v.children = nil + return ctx +} + +// NopComponent is a component that doesn't render anything. +var NopComponent = ComponentFunc(func(ctx context.Context, w io.Writer) error { return nil }) + +// GetChildren from the context. +func GetChildren(ctx context.Context) Component { + _, v := getContext(ctx) + if v.children == nil { + return NopComponent + } + return *v.children +} + +// EscapeString escapes HTML text within templates. +func EscapeString(s string) string { + return html.EscapeString(s) +} + +// Bool attribute value. +func Bool(value bool) bool { + return value +} + +// Classes for CSS. +// Supported types are string, ConstantCSSClass, ComponentCSSClass, map[string]bool. +func Classes(classes ...any) CSSClasses { + return CSSClasses(classes) +} + +// CSSClasses is a slice of CSS classes. +type CSSClasses []any + +// String returns the names of all CSS classes. +func (classes CSSClasses) String() string { + if len(classes) == 0 { + return "" + } + cp := newCSSProcessor() + for _, v := range classes { + cp.Add(v) + } + return cp.String() +} + +func newCSSProcessor() *cssProcessor { + return &cssProcessor{ + classNameToEnabled: make(map[string]bool), + } +} + +type cssProcessor struct { + classNameToEnabled map[string]bool + orderedNames []string +} + +func (cp *cssProcessor) Add(item any) { + switch c := item.(type) { + case []string: + for _, className := range c { + cp.AddClassName(className, true) + } + case string: + cp.AddClassName(c, true) + case ConstantCSSClass: + cp.AddClassName(c.ClassName(), true) + case ComponentCSSClass: + cp.AddClassName(c.ClassName(), true) + case map[string]bool: + // In Go, map keys are iterated in a randomized order. + // So the keys in the map must be sorted to produce consistent output. + keys := make([]string, len(c)) + var i int + for key := range c { + keys[i] = key + i++ + } + sort.Strings(keys) + for _, className := range keys { + cp.AddClassName(className, c[className]) + } + case []KeyValue[string, bool]: + for _, kv := range c { + cp.AddClassName(kv.Key, kv.Value) + } + case KeyValue[string, bool]: + cp.AddClassName(c.Key, c.Value) + case []KeyValue[CSSClass, bool]: + for _, kv := range c { + cp.AddClassName(kv.Key.ClassName(), kv.Value) + } + case KeyValue[CSSClass, bool]: + cp.AddClassName(c.Key.ClassName(), c.Value) + case CSSClasses: + for _, item := range c { + cp.Add(item) + } + case []CSSClass: + for _, item := range c { + cp.Add(item) + } + case func() CSSClass: + cp.AddClassName(c().ClassName(), true) + default: + cp.AddClassName(unknownTypeClassName, true) + } +} + +func (cp *cssProcessor) AddClassName(className string, enabled bool) { + cp.classNameToEnabled[className] = enabled + cp.orderedNames = append(cp.orderedNames, className) +} + +func (cp *cssProcessor) String() string { + // Order the outputs according to how they were input, and remove disabled names. + rendered := make(map[string]any, len(cp.classNameToEnabled)) + var names []string + for _, name := range cp.orderedNames { + if enabled := cp.classNameToEnabled[name]; !enabled { + continue + } + if _, hasBeenRendered := rendered[name]; hasBeenRendered { + continue + } + names = append(names, name) + rendered[name] = struct{}{} + } + + return strings.Join(names, " ") +} + +// KeyValue is a key and value pair. +type KeyValue[TKey comparable, TValue any] struct { + Key TKey `json:"name"` + Value TValue `json:"value"` +} + +// KV creates a new key/value pair from the input key and value. +func KV[TKey comparable, TValue any](key TKey, value TValue) KeyValue[TKey, TValue] { + return KeyValue[TKey, TValue]{ + Key: key, + Value: value, + } +} + +const unknownTypeClassName = "--templ-css-class-unknown-type" + +// Class returns a CSS class name. +// Deprecated: use a string instead. +func Class(name string) CSSClass { + return SafeClass(name) +} + +// SafeClass bypasses CSS class name validation. +// Deprecated: use a string instead. +func SafeClass(name string) CSSClass { + return ConstantCSSClass(name) +} + +// CSSClass provides a class name. +type CSSClass interface { + ClassName() string +} + +// ConstantCSSClass is a string constant of a CSS class name. +// Deprecated: use a string instead. +type ConstantCSSClass string + +// ClassName of the CSS class. +func (css ConstantCSSClass) ClassName() string { + return string(css) +} + +// ComponentCSSClass is a templ.CSS +type ComponentCSSClass struct { + // ID of the class, will be autogenerated. + ID string + // Definition of the CSS. + Class SafeCSS +} + +// ClassName of the CSS class. +func (css ComponentCSSClass) ClassName() string { + return css.ID +} + +// CSSID calculates an ID. +func CSSID(name string, css string) string { + sum := sha256.Sum256([]byte(css)) + hp := hex.EncodeToString(sum[:])[0:4] + // Benchmarking showed this was fastest, and with fewest allocations (1). + // Using strings.Builder (2 allocs). + // Using fmt.Sprintf (3 allocs). + return name + "_" + hp +} + +// NewCSSMiddleware creates HTTP middleware that renders a global stylesheet of ComponentCSSClass +// CSS if the request path matches, or updates the HTTP context to ensure that any handlers that +// use templ.Components skip rendering `); err != nil { + return err + } + } + return nil +} + +func renderCSSItemsToBuilder(sb *strings.Builder, v *contextValue, classes ...any) { + for _, c := range classes { + switch ccc := c.(type) { + case ComponentCSSClass: + if !v.hasClassBeenRendered(ccc.ID) { + sb.WriteString(string(ccc.Class)) + v.addClass(ccc.ID) + } + case KeyValue[ComponentCSSClass, bool]: + if !ccc.Value { + continue + } + renderCSSItemsToBuilder(sb, v, ccc.Key) + case KeyValue[CSSClass, bool]: + if !ccc.Value { + continue + } + renderCSSItemsToBuilder(sb, v, ccc.Key) + case CSSClasses: + renderCSSItemsToBuilder(sb, v, ccc...) + case []CSSClass: + for _, item := range ccc { + renderCSSItemsToBuilder(sb, v, item) + } + case func() CSSClass: + renderCSSItemsToBuilder(sb, v, ccc()) + case []string: + // Skip. These are class names, not CSS classes. + case string: + // Skip. This is a class name, not a CSS class. + case ConstantCSSClass: + // Skip. This is a class name, not a CSS class. + case CSSClass: + // Skip. This is a class name, not a CSS class. + case map[string]bool: + // Skip. These are class names, not CSS classes. + case KeyValue[string, bool]: + // Skip. These are class names, not CSS classes. + case []KeyValue[string, bool]: + // Skip. These are class names, not CSS classes. + case KeyValue[ConstantCSSClass, bool]: + // Skip. These are class names, not CSS classes. + case []KeyValue[ConstantCSSClass, bool]: + // Skip. These are class names, not CSS classes. + } + } +} + +// SafeCSS is CSS that has been sanitized. +type SafeCSS string + +type SafeCSSProperty string + +var safeCSSPropertyType = reflect.TypeOf(SafeCSSProperty("")) + +// SanitizeCSS sanitizes CSS properties to ensure that they are safe. +func SanitizeCSS[T ~string](property string, value T) SafeCSS { + if reflect.TypeOf(value) == safeCSSPropertyType { + return SafeCSS(safehtml.SanitizeCSSProperty(property) + ":" + string(value) + ";") + } + p, v := safehtml.SanitizeCSS(property, string(value)) + return SafeCSS(p + ":" + v + ";") +} + +// Attributes is an alias to map[string]any made for spread attributes. +type Attributes map[string]any + +// sortedKeys returns the keys of a map in sorted order. +func sortedKeys(m map[string]any) (keys []string) { + keys = make([]string, len(m)) + var i int + for k := range m { + keys[i] = k + i++ + } + sort.Strings(keys) + return keys +} + +func writeStrings(w io.Writer, ss ...string) (err error) { + for _, s := range ss { + if _, err = io.WriteString(w, s); err != nil { + return err + } + } + return nil +} + +func RenderAttributes(ctx context.Context, w io.Writer, attributes Attributes) (err error) { + for _, key := range sortedKeys(attributes) { + value := attributes[key] + switch value := value.(type) { + case string: + if err = writeStrings(w, ` `, EscapeString(key), `="`, EscapeString(value), `"`); err != nil { + return err + } + case *string: + if value != nil { + if err = writeStrings(w, ` `, EscapeString(key), `="`, EscapeString(*value), `"`); err != nil { + return err + } + } + case bool: + if value { + if err = writeStrings(w, ` `, EscapeString(key)); err != nil { + return err + } + } + case *bool: + if value != nil && *value { + if err = writeStrings(w, ` `, EscapeString(key)); err != nil { + return err + } + } + case KeyValue[string, bool]: + if value.Value { + if err = writeStrings(w, ` `, EscapeString(key), `="`, EscapeString(value.Key), `"`); err != nil { + return err + } + } + case KeyValue[bool, bool]: + if value.Value && value.Key { + if err = writeStrings(w, ` `, EscapeString(key)); err != nil { + return err + } + } + case func() bool: + if value() { + if err = writeStrings(w, ` `, EscapeString(key)); err != nil { + return err + } + } + } + } + return nil +} + +// Script handling. + +func safeEncodeScriptParams(escapeHTML bool, params []any) []string { + encodedParams := make([]string, len(params)) + for i := 0; i < len(encodedParams); i++ { + enc, _ := json.Marshal(params[i]) + if !escapeHTML { + encodedParams[i] = string(enc) + continue + } + encodedParams[i] = EscapeString(string(enc)) + } + return encodedParams +} + +// SafeScript encodes unknown parameters for safety for inside HTML attributes. +func SafeScript(functionName string, params ...any) string { + encodedParams := safeEncodeScriptParams(true, params) + sb := new(strings.Builder) + sb.WriteString(functionName) + sb.WriteRune('(') + sb.WriteString(strings.Join(encodedParams, ",")) + sb.WriteRune(')') + return sb.String() +} + +// SafeScript encodes unknown parameters for safety for inline scripts. +func SafeScriptInline(functionName string, params ...any) string { + encodedParams := safeEncodeScriptParams(false, params) + sb := new(strings.Builder) + sb.WriteString(functionName) + sb.WriteRune('(') + sb.WriteString(strings.Join(encodedParams, ",")) + sb.WriteRune(')') + return sb.String() +} + +type contextKeyType int + +const contextKey = contextKeyType(0) + +type contextValue struct { + ss map[string]struct{} + onceHandles map[*OnceHandle]struct{} + children *Component + nonce string +} + +func (v *contextValue) setHasBeenRendered(h *OnceHandle) { + if v.onceHandles == nil { + v.onceHandles = map[*OnceHandle]struct{}{} + } + v.onceHandles[h] = struct{}{} +} + +func (v *contextValue) getHasBeenRendered(h *OnceHandle) (ok bool) { + if v.onceHandles == nil { + v.onceHandles = map[*OnceHandle]struct{}{} + } + _, ok = v.onceHandles[h] + return +} + +func (v *contextValue) addScript(s string) { + if v.ss == nil { + v.ss = map[string]struct{}{} + } + v.ss["script_"+s] = struct{}{} +} + +func (v *contextValue) hasScriptBeenRendered(s string) (ok bool) { + if v.ss == nil { + v.ss = map[string]struct{}{} + } + _, ok = v.ss["script_"+s] + return +} + +func (v *contextValue) addClass(s string) { + if v.ss == nil { + v.ss = map[string]struct{}{} + } + v.ss["class_"+s] = struct{}{} +} + +func (v *contextValue) hasClassBeenRendered(s string) (ok bool) { + if v.ss == nil { + v.ss = map[string]struct{}{} + } + _, ok = v.ss["class_"+s] + return +} + +// InitializeContext initializes context used to store internal state used during rendering. +func InitializeContext(ctx context.Context) context.Context { + if _, ok := ctx.Value(contextKey).(*contextValue); ok { + return ctx + } + v := &contextValue{} + ctx = context.WithValue(ctx, contextKey, v) + return ctx +} + +func getContext(ctx context.Context) (context.Context, *contextValue) { + v, ok := ctx.Value(contextKey).(*contextValue) + if !ok { + ctx = InitializeContext(ctx) + v = ctx.Value(contextKey).(*contextValue) + } + return ctx, v +} + +// ComponentScript is a templ Script template. +type ComponentScript struct { + // Name of the script, e.g. print. + Name string + // Function to render. + Function string + // Call of the function in JavaScript syntax, including parameters, and + // ensures parameters are HTML escaped; useful for injecting into HTML + // attributes like onclick, onhover, etc. + // + // Given: + // functionName("some string",12345) + // It would render: + // __templ_functionName_sha("some string",12345)) + // + // This is can be injected into HTML attributes: + // + Call string + // Call of the function in JavaScript syntax, including parameters. It + // does not HTML escape parameters; useful for directly calling in script + // elements. + // + // Given: + // functionName("some string",12345) + // It would render: + // __templ_functionName_sha("some string",12345)) + // + // This is can be used to call the function inside a script tag: + // + CallInline string +} + +var _ Component = ComponentScript{} + +func writeScriptHeader(ctx context.Context, w io.Writer) (err error) { + var nonceAttr string + if nonce := GetNonce(ctx); nonce != "" { + nonceAttr = " nonce=\"" + EscapeString(nonce) + "\"" + } + _, err = fmt.Fprintf(w, ``); err != nil { + return err + } + } + return nil +} + +// RenderScriptItems renders a `); err != nil { + return err + } + } + return nil +} + +var bufferPool = sync.Pool{ + New: func() any { + return new(bytes.Buffer) + }, +} + +func GetBuffer() *bytes.Buffer { + return bufferPool.Get().(*bytes.Buffer) +} + +func ReleaseBuffer(b *bytes.Buffer) { + b.Reset() + bufferPool.Put(b) +} + +// JoinStringErrs joins an optional list of errors. +func JoinStringErrs(s string, errs ...error) (string, error) { + return s, errors.Join(errs...) +} + +// Error returned during template rendering. +type Error struct { + Err error + // FileName of the template file. + FileName string + // Line index of the error. + Line int + // Col index of the error. + Col int +} + +func (e Error) Error() string { + if e.FileName == "" { + e.FileName = "templ" + } + return fmt.Sprintf("%s: error at line %d, col %d: %v", e.FileName, e.Line, e.Col, e.Err) +} + +func (e Error) Unwrap() error { + return e.Err +} + +// Raw renders the input HTML to the output without applying HTML escaping. +// +// Use of this component presents a security risk - the HTML should come from +// a trusted source, because it will be included as-is in the output. +func Raw[T ~string](html T, errs ...error) Component { + return ComponentFunc(func(ctx context.Context, w io.Writer) (err error) { + if err = errors.Join(errs...); err != nil { + return err + } + _, err = io.WriteString(w, string(html)) + return err + }) +} + +// FromGoHTML creates a templ Component from a Go html/template template. +func FromGoHTML(t *template.Template, data any) Component { + return ComponentFunc(func(ctx context.Context, w io.Writer) (err error) { + return t.Execute(w, data) + }) +} + +// ToGoHTML renders the component to a Go html/template template.HTML string. +func ToGoHTML(ctx context.Context, c Component) (s template.HTML, err error) { + b := GetBuffer() + defer ReleaseBuffer(b) + if err = c.Render(ctx, b); err != nil { + return + } + s = template.HTML(b.String()) + return +} + +// WriteWatchModeString is used when rendering templates in development mode. +// the generator would have written non-go code to the _templ.txt file, which +// is then read by this function and written to the output. +func WriteWatchModeString(w io.Writer, lineNum int) error { + _, path, _, _ := runtime.Caller(1) + if !strings.HasSuffix(path, "_templ.go") { + return errors.New("templ: WriteWatchModeString can only be called from _templ.go") + } + txtFilePath := strings.Replace(path, "_templ.go", "_templ.txt", 1) + + literals, err := getWatchedStrings(txtFilePath) + if err != nil { + return fmt.Errorf("templ: failed to cache strings: %w", err) + } + + if lineNum > len(literals) { + return errors.New("templ: failed to find line " + strconv.Itoa(lineNum) + " in " + txtFilePath) + } + + unquoted, err := strconv.Unquote(`"` + literals[lineNum-1] + `"`) + if err != nil { + return err + } + _, err = io.WriteString(w, unquoted) + return err +} + +var ( + watchModeCache = map[string]watchState{} + watchStateMutex sync.Mutex +) + +type watchState struct { + modTime time.Time + strings []string +} + +func getWatchedStrings(txtFilePath string) ([]string, error) { + watchStateMutex.Lock() + defer watchStateMutex.Unlock() + + state, cached := watchModeCache[txtFilePath] + if !cached { + return cacheStrings(txtFilePath) + } + + if time.Since(state.modTime) < time.Millisecond*100 { + return state.strings, nil + } + + info, err := os.Stat(txtFilePath) + if err != nil { + return nil, fmt.Errorf("templ: failed to stat %s: %w", txtFilePath, err) + } + + if !info.ModTime().After(state.modTime) { + return state.strings, nil + } + + return cacheStrings(txtFilePath) +} + +func cacheStrings(txtFilePath string) ([]string, error) { + txtFile, err := os.Open(txtFilePath) + if err != nil { + return nil, fmt.Errorf("templ: failed to open %s: %w", txtFilePath, err) + } + defer txtFile.Close() + + info, err := txtFile.Stat() + if err != nil { + return nil, fmt.Errorf("templ: failed to stat %s: %w", txtFilePath, err) + } + + all, err := io.ReadAll(txtFile) + if err != nil { + return nil, fmt.Errorf("templ: failed to read %s: %w", txtFilePath, err) + } + + literals := strings.Split(string(all), "\n") + watchModeCache[txtFilePath] = watchState{ + modTime: info.ModTime(), + strings: literals, + } + + return literals, nil +} diff --git a/vendor/github.com/a-h/templ/safehtml/style.go b/vendor/github.com/a-h/templ/safehtml/style.go new file mode 100644 index 0000000..486df7c --- /dev/null +++ b/vendor/github.com/a-h/templ/safehtml/style.go @@ -0,0 +1,168 @@ +// Adapted from https://raw.githubusercontent.com/google/safehtml/3c4cd5b5d8c9a6c5882fba099979e9f50b65c876/style.go + +// Copyright (c) 2017 The Go Authors. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file or at +// https://developers.google.com/open-source/licenses/bsd + +package safehtml + +import ( + "net/url" + "regexp" + "strings" +) + +// SanitizeCSS attempts to sanitize CSS properties. +func SanitizeCSS(property, value string) (string, string) { + property = SanitizeCSSProperty(property) + if property == InnocuousPropertyName { + return InnocuousPropertyName, InnocuousPropertyValue + } + return property, SanitizeCSSValue(property, value) +} + +func SanitizeCSSValue(property, value string) string { + if sanitizer, ok := cssPropertyNameToValueSanitizer[property]; ok { + return sanitizer(value) + } + return sanitizeRegular(value) +} + +func SanitizeCSSProperty(property string) string { + if !identifierPattern.MatchString(property) { + return InnocuousPropertyName + } + return strings.ToLower(property) +} + +// identifierPattern matches a subset of valid values defined in +// https://www.w3.org/TR/css-syntax-3/#ident-token-diagram. This pattern matches all generic family name +// keywords defined in https://drafts.csswg.org/css-fonts-3/#family-name-value. +var identifierPattern = regexp.MustCompile(`^[-a-zA-Z]+$`) + +var cssPropertyNameToValueSanitizer = map[string]func(string) string{ + "background-image": sanitizeBackgroundImage, + "font-family": sanitizeFontFamily, + "display": sanitizeEnum, + "background-color": sanitizeRegular, + "background-position": sanitizeRegular, + "background-repeat": sanitizeRegular, + "background-size": sanitizeRegular, + "color": sanitizeRegular, + "height": sanitizeRegular, + "width": sanitizeRegular, + "left": sanitizeRegular, + "right": sanitizeRegular, + "top": sanitizeRegular, + "bottom": sanitizeRegular, + "font-weight": sanitizeRegular, + "padding": sanitizeRegular, + "z-index": sanitizeRegular, +} + +var validURLPrefixes = []string{ + `url("`, + `url('`, + `url(`, +} + +var validURLSuffixes = []string{ + `")`, + `')`, + `)`, +} + +func sanitizeBackgroundImage(v string) string { + // Check for <> as per https://github.com/google/safehtml/blob/be23134998433fcf0135dda53593fc8f8bf4df7c/style.go#L87C2-L89C3 + if strings.ContainsAny(v, "<>") { + return InnocuousPropertyValue + } + for _, u := range strings.Split(v, ",") { + u = strings.TrimSpace(u) + var found bool + for i, prefix := range validURLPrefixes { + if strings.HasPrefix(u, prefix) && strings.HasSuffix(u, validURLSuffixes[i]) { + found = true + u = strings.TrimPrefix(u, validURLPrefixes[i]) + u = strings.TrimSuffix(u, validURLSuffixes[i]) + break + } + } + if !found || !urlIsSafe(u) { + return InnocuousPropertyValue + } + } + return v +} + +func urlIsSafe(s string) bool { + u, err := url.Parse(s) + if err != nil { + return false + } + if u.IsAbs() { + if strings.EqualFold(u.Scheme, "http") || strings.EqualFold(u.Scheme, "https") || strings.EqualFold(u.Scheme, "mailto") { + return true + } + return false + } + return true +} + +var genericFontFamilyName = regexp.MustCompile(`^[a-zA-Z][- a-zA-Z]+$`) + +func sanitizeFontFamily(s string) string { + for _, f := range strings.Split(s, ",") { + f = strings.TrimSpace(f) + if strings.HasPrefix(f, `"`) { + if !strings.HasSuffix(f, `"`) { + return InnocuousPropertyValue + } + continue + } + if !genericFontFamilyName.MatchString(f) { + return InnocuousPropertyValue + } + } + return s +} + +func sanitizeEnum(s string) string { + if !safeEnumPropertyValuePattern.MatchString(s) { + return InnocuousPropertyValue + } + return s +} + +func sanitizeRegular(s string) string { + if !safeRegularPropertyValuePattern.MatchString(s) { + return InnocuousPropertyValue + } + return s +} + +// InnocuousPropertyName is an innocuous property generated by a sanitizer when its input is unsafe. +const InnocuousPropertyName = "zTemplUnsafeCSSPropertyName" + +// InnocuousPropertyValue is an innocuous property generated by a sanitizer when its input is unsafe. +const InnocuousPropertyValue = "zTemplUnsafeCSSPropertyValue" + +// safeRegularPropertyValuePattern matches strings that are safe to use as property values. +// Specifically, it matches string where every '*' or '/' is followed by end-of-text or a safe rune +// (i.e. alphanumerics or runes in the set [+-.!#%_ \t]). This regex ensures that the following +// are disallowed: +// - "/*" and "*/", which are CSS comment markers. +// - "//", even though this is not a comment marker in the CSS specification. Disallowing +// this string minimizes the chance that browser peculiarities or parsing bugs will allow +// sanitization to be bypassed. +// - '(' and ')', which can be used to call functions. +// - ',', since it can be used to inject extra values into a property. +// - Runes which could be matched on CSS error recovery of a previously malformed token, such as '@' +// and ':'. See http://www.w3.org/TR/css3-syntax/#error-handling. +var safeRegularPropertyValuePattern = regexp.MustCompile(`^(?:[*/]?(?:[0-9a-zA-Z+-.!#%_ \t]|$))*$`) + +// safeEnumPropertyValuePattern matches strings that are safe to use as enumerated property values. +// Specifically, it matches strings that contain only alphabetic and '-' runes. +var safeEnumPropertyValuePattern = regexp.MustCompile(`^[a-zA-Z-]*$`) diff --git a/vendor/github.com/a-h/templ/templ.png b/vendor/github.com/a-h/templ/templ.png new file mode 100644 index 0000000..1c4bc2f Binary files /dev/null and b/vendor/github.com/a-h/templ/templ.png differ diff --git a/vendor/github.com/a-h/templ/url.go b/vendor/github.com/a-h/templ/url.go new file mode 100644 index 0000000..bf912e1 --- /dev/null +++ b/vendor/github.com/a-h/templ/url.go @@ -0,0 +1,20 @@ +package templ + +import "strings" + +// FailedSanitizationURL is returned if a URL fails sanitization checks. +const FailedSanitizationURL = SafeURL("about:invalid#TemplFailedSanitizationURL") + +// URL sanitizes the input string s and returns a SafeURL. +func URL(s string) SafeURL { + if i := strings.IndexRune(s, ':'); i >= 0 && !strings.ContainsRune(s[:i], '/') { + protocol := s[:i] + if !strings.EqualFold(protocol, "http") && !strings.EqualFold(protocol, "https") && !strings.EqualFold(protocol, "mailto") && !strings.EqualFold(protocol, "tel") && !strings.EqualFold(protocol, "ftp") && !strings.EqualFold(protocol, "ftps") { + return FailedSanitizationURL + } + } + return SafeURL(s) +} + +// SafeURL is a URL that has been sanitized. +type SafeURL string diff --git a/vendor/github.com/a-h/templ/version.go b/vendor/github.com/a-h/templ/version.go new file mode 100644 index 0000000..b7fbb6f --- /dev/null +++ b/vendor/github.com/a-h/templ/version.go @@ -0,0 +1,10 @@ +package templ + +import _ "embed" + +//go:embed .version +var version string + +func Version() string { + return "v" + version +} diff --git a/vendor/github.com/darklab8/fl-darkstat/LICENSE b/vendor/github.com/darklab8/fl-darkstat/LICENSE new file mode 100644 index 0000000..693e6a0 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/LICENSE @@ -0,0 +1,667 @@ +fl-darkstat was originally created by Andrei Novoselov (aka darkwind, aka dd84ai) +Visit dark tools web site: https://darklab8.github.io/blog/pet_projects.html#DiscoveryFreelancercommunity + +You must give appropriate credit to original creator and provide a link to the license. +The work is licensed under AGPL license. See it below. + +GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + +Copyright (C) 2007 Free Software Foundation, Inc. +Everyone is permitted to copy and distribute verbatim copies +of this license document, but changing it is not allowed. + + Preamble + +The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + +The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. + +When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + +Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + +A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + +The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + +An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + +The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + +0. Definitions. + +"This License" refers to version 3 of the GNU Affero General Public License. + +"Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + +"The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + +To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + +A "covered work" means either the unmodified Program or a work based +on the Program. + +To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + +To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + +An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + +1. Source Code. + +The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + +A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + +The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + +The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + +The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + +The Corresponding Source for a work in source code form is that +same work. + +2. Basic Permissions. + +All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + +You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + +Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + +3. Protecting Users' Legal Rights From Anti-Circumvention Law. + +No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + +When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + +4. Conveying Verbatim Copies. + +You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + +You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + +5. Conveying Modified Source Versions. + +You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + +a) The work must carry prominent notices stating that you modified +it, and giving a relevant date. + +b) The work must carry prominent notices stating that it is +released under this License and any conditions added under section +7. This requirement modifies the requirement in section 4 to +"keep intact all notices". + +c) You must license the entire work, as a whole, under this +License to anyone who comes into possession of a copy. This +License will therefore apply, along with any applicable section 7 +additional terms, to the whole of the work, and all its parts, +regardless of how they are packaged. This License gives no +permission to license the work in any other way, but it does not +invalidate such permission if you have separately received it. + +d) If the work has interactive user interfaces, each must display +Appropriate Legal Notices; however, if the Program has interactive +interfaces that do not display Appropriate Legal Notices, your +work need not make them do so. + +A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + +6. Conveying Non-Source Forms. + +You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + +a) Convey the object code in, or embodied in, a physical product +(including a physical distribution medium), accompanied by the +Corresponding Source fixed on a durable physical medium +customarily used for software interchange. + +b) Convey the object code in, or embodied in, a physical product +(including a physical distribution medium), accompanied by a +written offer, valid for at least three years and valid for as +long as you offer spare parts or customer support for that product +model, to give anyone who possesses the object code either (1) a +copy of the Corresponding Source for all the software in the +product that is covered by this License, on a durable physical +medium customarily used for software interchange, for a price no +more than your reasonable cost of physically performing this +conveying of source, or (2) access to copy the +Corresponding Source from a network server at no charge. + +c) Convey individual copies of the object code with a copy of the +written offer to provide the Corresponding Source. This +alternative is allowed only occasionally and noncommercially, and +only if you received the object code with such an offer, in accord +with subsection 6b. + +d) Convey the object code by offering access from a designated +place (gratis or for a charge), and offer equivalent access to the +Corresponding Source in the same way through the same place at no +further charge. You need not require recipients to copy the +Corresponding Source along with the object code. If the place to +copy the object code is a network server, the Corresponding Source +may be on a different server (operated by you or a third party) +that supports equivalent copying facilities, provided you maintain +clear directions next to the object code saying where to find the +Corresponding Source. Regardless of what server hosts the +Corresponding Source, you remain obligated to ensure that it is +available for as long as needed to satisfy these requirements. + +e) Convey the object code using peer-to-peer transmission, provided +you inform other peers where the object code and Corresponding +Source of the work are being offered to the general public at no +charge under subsection 6d. + +A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + +A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + +"Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + +If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + +The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + +Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + +7. Additional Terms. + +"Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + +When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + +Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + +a) Disclaiming warranty or limiting liability differently from the +terms of sections 15 and 16 of this License; or + +b) Requiring preservation of specified reasonable legal notices or +author attributions in that material or in the Appropriate Legal +Notices displayed by works containing it; or + +c) Prohibiting misrepresentation of the origin of that material, or +requiring that modified versions of such material be marked in +reasonable ways as different from the original version; or + +d) Limiting the use for publicity purposes of names of licensors or +authors of the material; or + +e) Declining to grant rights under trademark law for use of some +trade names, trademarks, or service marks; or + +f) Requiring indemnification of licensors and authors of that +material by anyone who conveys the material (or modified versions of +it) with contractual assumptions of liability to the recipient, for +any liability that these contractual assumptions directly impose on +those licensors and authors. + +All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + +If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + +Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + +8. Termination. + +You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + +However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + +Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + +Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + +9. Acceptance Not Required for Having Copies. + +You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + +10. Automatic Licensing of Downstream Recipients. + +Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + +An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + +You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + +11. Patents. + +A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + +A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + +Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + +In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + +If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + +If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + +A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + +Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + +12. No Surrender of Others' Freedom. + +If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + +13. Remote Network Interaction; Use with the GNU General Public License. + +Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + +Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the work with which it is combined will remain governed by version +3 of the GNU General Public License. + +14. Revised Versions of this License. + +The Free Software Foundation may publish revised and/or new versions of +the GNU Affero General Public License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU Affero General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU Affero General Public License, you may choose any version ever published +by the Free Software Foundation. + +If the Program specifies that a proxy can decide which future +versions of the GNU Affero General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + +Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + +15. Disclaimer of Warranty. + +THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +16. Limitation of Liability. + +IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + +17. Interpretation of Sections 15 and 16. + +If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + +How to Apply These Terms to Your New Programs + +If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + +To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + +fl-darkstat - web tool to navigate game data in space simulator Freelancer +Copyright (C) 2024 Andrei Novoselov https://github.com/darklab8/fl-darkstat + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published +by the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + +If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for the +specific requirements. + +You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU AGPL, see +. \ No newline at end of file diff --git a/vendor/github.com/darklab8/fl-darkstat/configs/LICENSE b/vendor/github.com/darklab8/fl-darkstat/configs/LICENSE new file mode 100644 index 0000000..e5a9366 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/configs/LICENSE @@ -0,0 +1,667 @@ +fl-configs was originally created by Andrei Novoselov (aka darkwind, aka dd84ai) +Visit dark tools web site: https://darklab8.github.io/blog/pet_projects.html#DiscoveryFreelancercommunity + +You must give appropriate credit to original creator and provide a link to the license. +The work is licensed under AGPL license. See it below. + +GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + +Copyright (C) 2007 Free Software Foundation, Inc. +Everyone is permitted to copy and distribute verbatim copies +of this license document, but changing it is not allowed. + + Preamble + +The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + +The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. + +When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + +Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + +A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + +The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + +An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + +The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + +0. Definitions. + +"This License" refers to version 3 of the GNU Affero General Public License. + +"Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + +"The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + +To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + +A "covered work" means either the unmodified Program or a work based +on the Program. + +To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + +To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + +An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + +1. Source Code. + +The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + +A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + +The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + +The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + +The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + +The Corresponding Source for a work in source code form is that +same work. + +2. Basic Permissions. + +All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + +You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + +Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + +3. Protecting Users' Legal Rights From Anti-Circumvention Law. + +No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + +When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + +4. Conveying Verbatim Copies. + +You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + +You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + +5. Conveying Modified Source Versions. + +You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + +a) The work must carry prominent notices stating that you modified +it, and giving a relevant date. + +b) The work must carry prominent notices stating that it is +released under this License and any conditions added under section +7. This requirement modifies the requirement in section 4 to +"keep intact all notices". + +c) You must license the entire work, as a whole, under this +License to anyone who comes into possession of a copy. This +License will therefore apply, along with any applicable section 7 +additional terms, to the whole of the work, and all its parts, +regardless of how they are packaged. This License gives no +permission to license the work in any other way, but it does not +invalidate such permission if you have separately received it. + +d) If the work has interactive user interfaces, each must display +Appropriate Legal Notices; however, if the Program has interactive +interfaces that do not display Appropriate Legal Notices, your +work need not make them do so. + +A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + +6. Conveying Non-Source Forms. + +You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + +a) Convey the object code in, or embodied in, a physical product +(including a physical distribution medium), accompanied by the +Corresponding Source fixed on a durable physical medium +customarily used for software interchange. + +b) Convey the object code in, or embodied in, a physical product +(including a physical distribution medium), accompanied by a +written offer, valid for at least three years and valid for as +long as you offer spare parts or customer support for that product +model, to give anyone who possesses the object code either (1) a +copy of the Corresponding Source for all the software in the +product that is covered by this License, on a durable physical +medium customarily used for software interchange, for a price no +more than your reasonable cost of physically performing this +conveying of source, or (2) access to copy the +Corresponding Source from a network server at no charge. + +c) Convey individual copies of the object code with a copy of the +written offer to provide the Corresponding Source. This +alternative is allowed only occasionally and noncommercially, and +only if you received the object code with such an offer, in accord +with subsection 6b. + +d) Convey the object code by offering access from a designated +place (gratis or for a charge), and offer equivalent access to the +Corresponding Source in the same way through the same place at no +further charge. You need not require recipients to copy the +Corresponding Source along with the object code. If the place to +copy the object code is a network server, the Corresponding Source +may be on a different server (operated by you or a third party) +that supports equivalent copying facilities, provided you maintain +clear directions next to the object code saying where to find the +Corresponding Source. Regardless of what server hosts the +Corresponding Source, you remain obligated to ensure that it is +available for as long as needed to satisfy these requirements. + +e) Convey the object code using peer-to-peer transmission, provided +you inform other peers where the object code and Corresponding +Source of the work are being offered to the general public at no +charge under subsection 6d. + +A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + +A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + +"Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + +If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + +The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + +Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + +7. Additional Terms. + +"Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + +When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + +Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + +a) Disclaiming warranty or limiting liability differently from the +terms of sections 15 and 16 of this License; or + +b) Requiring preservation of specified reasonable legal notices or +author attributions in that material or in the Appropriate Legal +Notices displayed by works containing it; or + +c) Prohibiting misrepresentation of the origin of that material, or +requiring that modified versions of such material be marked in +reasonable ways as different from the original version; or + +d) Limiting the use for publicity purposes of names of licensors or +authors of the material; or + +e) Declining to grant rights under trademark law for use of some +trade names, trademarks, or service marks; or + +f) Requiring indemnification of licensors and authors of that +material by anyone who conveys the material (or modified versions of +it) with contractual assumptions of liability to the recipient, for +any liability that these contractual assumptions directly impose on +those licensors and authors. + +All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + +If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + +Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + +8. Termination. + +You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + +However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + +Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + +Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + +9. Acceptance Not Required for Having Copies. + +You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + +10. Automatic Licensing of Downstream Recipients. + +Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + +An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + +You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + +11. Patents. + +A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + +A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + +Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + +In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + +If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + +If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + +A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + +Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + +12. No Surrender of Others' Freedom. + +If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + +13. Remote Network Interaction; Use with the GNU General Public License. + +Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + +Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the work with which it is combined will remain governed by version +3 of the GNU General Public License. + +14. Revised Versions of this License. + +The Free Software Foundation may publish revised and/or new versions of +the GNU Affero General Public License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU Affero General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU Affero General Public License, you may choose any version ever published +by the Free Software Foundation. + +If the Program specifies that a proxy can decide which future +versions of the GNU Affero General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + +Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + +15. Disclaimer of Warranty. + +THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +16. Limitation of Liability. + +IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + +17. Interpretation of Sections 15 and 16. + +If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + +How to Apply These Terms to Your New Programs + +If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + +To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + +fl-configs - ORM data accessing lib for Freelancer game configs +Copyright (C) 2024 Andrei Novoselov https://github.com/darklab8/fl-configs + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published +by the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + +If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for the +specific requirements. + +You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU AGPL, see +. \ No newline at end of file diff --git a/vendor/github.com/darklab8/fl-darkstat/configs/cfg/param_key.go b/vendor/github.com/darklab8/fl-darkstat/configs/cfg/param_key.go new file mode 100644 index 0000000..dc1c2cd --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/configs/cfg/param_key.go @@ -0,0 +1,16 @@ +package cfg + +type ParamKey string + +type Key = ParamKey + +// var contexts = sync.Map{} + +// func Key(key string) ParamKey { +// value, ok := contexts.Load(key) +// if !ok { +// value = ParamKey(ptr.Ptr(key)) +// contexts.Store(key, value) +// } +// return value.(ParamKey) +// } diff --git a/vendor/github.com/darklab8/fl-darkstat/configs/cfg/path_unix.go b/vendor/github.com/darklab8/fl-darkstat/configs/cfg/path_unix.go new file mode 100644 index 0000000..68621cb --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/configs/cfg/path_unix.go @@ -0,0 +1,5 @@ +//go:build !windows + +package cfg + +const IsLinux = true diff --git a/vendor/github.com/darklab8/fl-darkstat/configs/cfg/path_windows.go b/vendor/github.com/darklab8/fl-darkstat/configs/cfg/path_windows.go new file mode 100644 index 0000000..799675d --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/configs/cfg/path_windows.go @@ -0,0 +1,5 @@ +//go:build windows + +package cfg + +const IsLinux = false diff --git a/vendor/github.com/darklab8/fl-darkstat/configs/cfg/types.go b/vendor/github.com/darklab8/fl-darkstat/configs/cfg/types.go new file mode 100644 index 0000000..4ed8459 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/configs/cfg/types.go @@ -0,0 +1,48 @@ +package cfg + +import "github.com/darklab8/go-utils/utils/ptr" + +type Vector struct { + X float64 + Y float64 + Z float64 +} + +type TractorID string + +type FactionNick string + +type Milliseconds = float64 +type Seconds = float64 + +type MillisecondsI = int +type SecondsI = int + +type BaseUniNick string + +func (b BaseUniNick) ToStr() string { return string(b) } + +type ShipClass int64 + +func (s ShipClass) ToStr() string { + switch s { + case 10: + return "liner" + case 14: + return "miner" + default: + return "" + } +} + +// Gob friendly +type ErrP string +type Err = *ErrP + +func NewErr(msg string) Err { + return Err(ptr.Ptr(msg)) +} + +func (r *ErrP) Error() string { + return string(*r) +} diff --git a/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/config_mapped_methods.go b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/config_mapped_methods.go new file mode 100644 index 0000000..72a00d3 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/config_mapped_methods.go @@ -0,0 +1,12 @@ +package configs_mapped + +func (configs *MappedConfigs) CraftableBaseName() string { + if configs.Discovery != nil { + return "PoB crafts" + } + if configs.FLSR != nil { + return "Craftable" + } + + return "NoCrafts" +} diff --git a/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/export_helpers.go b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/export_helpers.go new file mode 100644 index 0000000..e069167 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/export_helpers.go @@ -0,0 +1,36 @@ +package configs_mapped + +import ( + "fmt" + "strings" + + "github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/universe_mapped" + "github.com/darklab8/fl-darkstat/configs/configs_settings" +) + +func (configs *MappedConfigs) GetInfocardName(ids_name int, nickname string) string { + if configs_settings.Env.FallbackInfonamesToNickname { + return fmt.Sprintf("[%s]", nickname) + } + + if infoname, ok := configs.Infocards.Infonames[ids_name]; ok { + return strings.ReplaceAll(string(infoname), "\r", "") + } else { + return fmt.Sprintf("[%s]", nickname) + } +} + +func (configs *MappedConfigs) GetRegionName(system *universe_mapped.System) string { + var Region string + system_infocard_Id := system.Ids_info.Get() + if value, ok := configs.Infocards.Infocards[system_infocard_Id]; ok { + if len(value.Lines) > 0 { + Region = value.Lines[0] + } + } + + if strings.Contains(Region, "Sometimes limbo") && len(Region) > 11 { + Region = Region[:20] + "..." + } + return Region +} diff --git a/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/fixtures.go b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/fixtures.go new file mode 100644 index 0000000..0f9adf3 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/fixtures.go @@ -0,0 +1,18 @@ +package configs_mapped + +import ( + "github.com/darklab8/fl-darkstat/configs/configs_settings" +) + +var parsed *MappedConfigs = nil + +func TestFixtureConfigs() *MappedConfigs { + if parsed != nil { + return parsed + } + + game_location := configs_settings.Env.FreelancerFolder + parsed = NewMappedConfigs().Read(game_location) + + return parsed +} diff --git a/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/flsr/flsr_recipes/recipes.go b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/flsr/flsr_recipes/recipes.go new file mode 100644 index 0000000..8edad33 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/flsr/flsr_recipes/recipes.go @@ -0,0 +1,57 @@ +package flsr_recipes + +import ( + "github.com/darklab8/fl-darkstat/configs/cfg" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/filefind/file" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/iniload" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/semantic" + "github.com/darklab8/go-utils/utils/utils_types" +) + +type Recipe struct { + semantic.Model + Product *semantic.String +} + +type Config struct { + *iniload.IniLoader + Products []*Recipe + ProductsByNick map[string][]*Recipe +} + +const ( + FILENAME utils_types.FilePath = "flsr-crafting.cfg" +) + +func Read(input_file *iniload.IniLoader) *Config { + frelconfig := &Config{ + IniLoader: input_file, + ProductsByNick: make(map[string][]*Recipe), + } + + for _, section := range input_file.Sections { + + recipe := &Recipe{ + Product: semantic.NewString(section, cfg.Key("product"), semantic.WithLowercaseS(), semantic.WithoutSpacesS()), + } + recipe.Map(section) + + _, is_product := recipe.Product.GetValue() + if !is_product { + continue + } + + frelconfig.Products = append(frelconfig.Products, recipe) + frelconfig.ProductsByNick[recipe.Product.Get()] = append(frelconfig.ProductsByNick[recipe.Product.Get()], recipe) + + } + + return frelconfig + +} + +func (frelconfig *Config) Write() *file.File { + inifile := frelconfig.Render() + inifile.Write(inifile.File) + return inifile.File +} diff --git a/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/const_mapped/consts.go b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/const_mapped/consts.go new file mode 100644 index 0000000..e197f8b --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/const_mapped/consts.go @@ -0,0 +1,57 @@ +package const_mapped + +import ( + "github.com/darklab8/fl-darkstat/configs/cfg" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/filefind/file" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/iniload" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/semantic" +) + +const ( + FILENAME = "constants.ini" +) + +type ShieldEquipConsts struct { + semantic.Model + HULL_DAMAGE_FACTOR *semantic.Float +} + +type EngineEquipConsts struct { + semantic.Model + CRUISING_SPEED *semantic.Int +} + +type Config struct { + *iniload.IniLoader + + ShieldEquipConsts *ShieldEquipConsts + EngineEquipConsts *EngineEquipConsts +} + +func Read(input_file *iniload.IniLoader) *Config { + frelconfig := &Config{ + IniLoader: input_file, + } + if groups, ok := frelconfig.SectionMap["[shieldequipconsts]"]; ok { + shield_consts := &ShieldEquipConsts{} + shield_consts.Map(groups[0]) + shield_consts.HULL_DAMAGE_FACTOR = semantic.NewFloat(groups[0], cfg.Key("hull_damage_factor"), semantic.Precision(2)) + + frelconfig.ShieldEquipConsts = shield_consts + } + if groups, ok := frelconfig.SectionMap["[engineequipconsts]"]; ok { + const_group := &EngineEquipConsts{} + const_group.Map(groups[0]) + const_group.CRUISING_SPEED = semantic.NewInt(groups[0], cfg.Key("cruising_speed")) + + frelconfig.EngineEquipConsts = const_group + } + + return frelconfig +} + +func (frelconfig *Config) Write() *file.File { + inifile := frelconfig.Render() + inifile.Write(inifile.File) + return inifile.File +} diff --git a/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/equipment_mapped/equip_mapped/equip.go b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/equipment_mapped/equip_mapped/equip.go new file mode 100644 index 0000000..5ab7842 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/equipment_mapped/equip_mapped/equip.go @@ -0,0 +1,642 @@ +package equip_mapped + +import ( + "strings" + + "github.com/darklab8/fl-darkstat/configs/cfg" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/filefind/file" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/iniload" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/semantic" + "github.com/darklab8/go-utils/utils/utils_types" +) + +type Item struct { + semantic.Model + + Category string + Nickname *semantic.String + IdsName *semantic.Int + IdsInfo *semantic.Int +} + +type Commodity struct { + semantic.Model + + Nickname *semantic.String + IdsName *semantic.Int + IdsInfo *semantic.Int + UnitsPerContainer *semantic.Int + PodApperance *semantic.String + LootAppearance *semantic.String + DecayPerSecond *semantic.Int + HitPts *semantic.Int + Mass *semantic.Float + + Volumes []*Volume +} +type Volume struct { + semantic.Model + ShipClass *semantic.Int + Volume *semantic.Float +} + +func (volume Volume) GetShipClass() cfg.ShipClass { + if value, ok := volume.ShipClass.GetValue(); ok { + return cfg.ShipClass(value) + } + + return -1 +} + +type Munition struct { + semantic.Model + Nickname *semantic.String + ExplosionArch *semantic.String + RequiredAmmo *semantic.Bool + HullDamage *semantic.Int + EnergyDamange *semantic.Int + HealintAmount *semantic.Int + WeaponType *semantic.String + Motor *semantic.String + MaxAngularVelocity *semantic.Float + + HitPts *semantic.Int + AmmoLimitAmountInCatridge *semantic.Int + AmmoLimitMaxCatridges *semantic.Int + Volume *semantic.Float + + IdsName *semantic.Int + IdsInfo *semantic.Int + + ConstEffect *semantic.String + MunitionHitEffect *semantic.String + + LifeTime *semantic.Float + SeekerType *semantic.String + SeekerRange *semantic.Int + SeekerFovDeg *semantic.Int + Mass *semantic.Float + + ArmorPen *semantic.Float // Disco only +} + +type Explosion struct { + semantic.Model + Nickname *semantic.String + HullDamage *semantic.Int + EnergyDamange *semantic.Int + Radius *semantic.Int + + ArmorPen *semantic.Float // Disco only +} + +type Gun struct { + semantic.Model + Nickname *semantic.String + IdsName *semantic.Int + IdsInfo *semantic.Int + HitPts *semantic.String // not able to read hit_pts = 5E+13 as any number yet + PowerUsage *semantic.Float + RefireDelay *semantic.Float + MuzzleVelosity *semantic.Float + Toughness *semantic.Float + IsAutoTurret *semantic.Bool + TurnRate *semantic.Float + ProjectileArchetype *semantic.String + HPGunType *semantic.String + Lootable *semantic.Bool + DispersionAngle *semantic.Float + Volume *semantic.Float + Mass *semantic.Float + + FlashParticleName *semantic.String + + BurstAmmo *semantic.Int + BurstReload *semantic.Float + NumBarrels *semantic.Int +} + +type Mine struct { + semantic.Model + Nickname *semantic.String + ExplosionArch *semantic.String + AmmoLimitAmountInCatridge *semantic.Int + AmmoLimitMaxCatridges *semantic.Int + HitPts *semantic.Int + Lifetime *semantic.Float + IdsName *semantic.Int + IdsInfo *semantic.Int + SeekDist *semantic.Int + TopSpeed *semantic.Int + Acceleration *semantic.Int + OwnerSafeTime *semantic.Int + DetonationDistance *semantic.Int + LinearDrag *semantic.Float + Mass *semantic.Float +} + +type MineDropper struct { + semantic.Model + + Nickname *semantic.String + IdsName *semantic.Int + IdsInfo *semantic.Int + HitPts *semantic.Int + ChildImpulse *semantic.Float + PowerUsage *semantic.Float + RefireDelay *semantic.Float + MuzzleVelocity *semantic.Float + Toughness *semantic.Float + ProjectileArchetype *semantic.String + Lootable *semantic.Bool + Mass *semantic.Float +} + +type ShieldGenerator struct { + semantic.Model + + Nickname *semantic.String + IdsName *semantic.Int + IdsInfo *semantic.Int + HitPts *semantic.Int + Volume *semantic.Int + RegenerationRate *semantic.Int + MaxCapacity *semantic.Int + Toughness *semantic.Float + HpType *semantic.String + ConstPowerDraw *semantic.Int + RebuildPowerDraw *semantic.Int + OfflineRebuildTime *semantic.Int + Lootable *semantic.Bool + ShieldType *semantic.String + Mass *semantic.Float +} + +type CloakingDevice struct { + semantic.Model + + Nickname *semantic.String + IdsName *semantic.Int + IdsInfo *semantic.Int + HitPts *semantic.Int + Volume *semantic.Float + PowerUsage *semantic.Float + CloakInTime *semantic.Int + CloakOutTime *semantic.Int +} + +type Thruster struct { + semantic.Model + + Nickname *semantic.String + IdsName *semantic.Int + IdsInfo *semantic.Int + HitPts *semantic.Int + Lootable *semantic.Bool + + MaxForce *semantic.Int + PowerUsage *semantic.Int + Mass *semantic.Float +} + +type Engine struct { + semantic.Model + Nickname *semantic.String + IdsName *semantic.Int + IdsInfo *semantic.Int + CruiseSpeed *semantic.Int + LinearDrag *semantic.Int + MaxForce *semantic.Int + ReverseFraction *semantic.Float + + HpType *semantic.String + FlameEffect *semantic.String + TrailEffect *semantic.String + CruiseChargeTime *semantic.Int + Mass *semantic.Float +} + +type Power struct { + semantic.Model + Nickname *semantic.String + IdsName *semantic.Int + IdsInfo *semantic.Int + Capacity *semantic.Int + ChargeRate *semantic.Int + ThrustCapacity *semantic.Int + ThrustRecharge *semantic.Int + Mass *semantic.Float +} + +type Tractor struct { + semantic.Model + Nickname *semantic.String + IdsName *semantic.Int + IdsInfo *semantic.Int + MaxLength *semantic.Int + ReachSpeed *semantic.Int + Lootable *semantic.Bool + Mass *semantic.Float +} + +type CounterMeasureDropper struct { + semantic.Model + Nickname *semantic.String + IdsName *semantic.Int + IdsInfo *semantic.Int + Lootable *semantic.Bool + + ProjectileArchetype *semantic.String + HitPts *semantic.Int + AIRange *semantic.Int + Mass *semantic.Float +} + +type CounterMeasure struct { + semantic.Model + Nickname *semantic.String + IdsName *semantic.Int + IdsInfo *semantic.Int + AmmoLimitAmountInCatridge *semantic.Int + AmmoLimitMaxCatridges *semantic.Int + Lifetime *semantic.Int + Range *semantic.Int + DiversionPctg *semantic.Int + Mass *semantic.Float +} + +type Scanner struct { + semantic.Model + Nickname *semantic.String + IdsName *semantic.Int + IdsInfo *semantic.Int + Range *semantic.Int + CargoScanRange *semantic.Int + Lootable *semantic.Bool + Mass *semantic.Float +} + +type Config struct { + Files []*iniload.IniLoader + + Commodities []*Commodity + CommoditiesMap map[string]*Commodity + + Guns []*Gun + GunMap map[string]*Gun + Munitions []*Munition + MunitionMap map[string]*Munition + + Explosions []*Explosion + ExplosionMap map[string]*Explosion + + MineDroppers []*MineDropper + Mines []*Mine + MinesMap map[string]*Mine + + Items []*Item + ItemsMap map[string]*Item + + ShieldGens []*ShieldGenerator + ShidGenMap map[string]*ShieldGenerator + Thrusters []*Thruster + ThrusterMap map[string]*Thruster + + Engines []*Engine + EnginesMap map[string]*Engine + Powers []*Power + PowersMap map[string]*Power + + CounterMeasureDroppers []*CounterMeasureDropper + CounterMeasure []*CounterMeasure + CounterMeasureMap map[string]*CounterMeasure + + Scanners []*Scanner + + Tractors []*Tractor + Cloaks []*CloakingDevice +} + +const ( + FILENAME_SELECT_EQUIP utils_types.FilePath = "select_equip.ini" +) + +func Read(files []*iniload.IniLoader) *Config { + frelconfig := &Config{ + Files: files, + Guns: make([]*Gun, 0, 100), + Munitions: make([]*Munition, 0, 100), + MineDroppers: make([]*MineDropper, 0, 100), + MunitionMap: make(map[string]*Munition), + ExplosionMap: make(map[string]*Explosion), + MinesMap: make(map[string]*Mine), + EnginesMap: make(map[string]*Engine), + PowersMap: make(map[string]*Power), + CounterMeasureMap: make(map[string]*CounterMeasure), + GunMap: make(map[string]*Gun), + ShidGenMap: make(map[string]*ShieldGenerator), + ThrusterMap: make(map[string]*Thruster), + } + frelconfig.Commodities = make([]*Commodity, 0, 100) + frelconfig.CommoditiesMap = make(map[string]*Commodity) + frelconfig.Items = make([]*Item, 0, 100) + frelconfig.ItemsMap = make(map[string]*Item) + + for _, file := range files { + for _, section := range file.Sections { + item := &Item{} + item.Map(section) + item.Category = strings.ToLower(strings.ReplaceAll(strings.ReplaceAll(string(section.Type), "[", ""), "]", "")) + item.Nickname = semantic.NewString(section, cfg.Key("nickname"), semantic.OptsS(semantic.Optional()), semantic.WithLowercaseS(), semantic.WithoutSpacesS()) + item.IdsName = semantic.NewInt(section, cfg.Key("ids_name"), semantic.Optional()) + item.IdsInfo = semantic.NewInt(section, cfg.Key("ids_info"), semantic.Optional()) + frelconfig.Items = append(frelconfig.Items, item) + frelconfig.ItemsMap[item.Nickname.Get()] = item + + switch section.Type { + case "[commodity]": + commodity := &Commodity{ + Mass: semantic.NewFloat(section, cfg.Key("mass"), semantic.Precision(2)), + } + commodity.Map(section) + commodity.Nickname = semantic.NewString(section, cfg.Key("nickname"), semantic.WithLowercaseS(), semantic.WithoutSpacesS()) + commodity.IdsName = semantic.NewInt(section, cfg.Key("ids_name"), semantic.Optional()) + commodity.IdsInfo = semantic.NewInt(section, cfg.Key("ids_info"), semantic.Optional()) + commodity.UnitsPerContainer = semantic.NewInt(section, cfg.Key("units_per_container")) + commodity.PodApperance = semantic.NewString(section, cfg.Key("pod_appearance")) + commodity.LootAppearance = semantic.NewString(section, cfg.Key("loot_appearance")) + commodity.DecayPerSecond = semantic.NewInt(section, cfg.Key("decay_per_second")) + commodity.HitPts = semantic.NewInt(section, cfg.Key("hit_pts")) + + // commodity.Volume = semantic.NewFloat(section, cfg.Key("volume", semantic.Precision(6)) + override := &Volume{ + ShipClass: semantic.NewInt(section, cfg.Key("volume"), semantic.Order(1)), // does not exist. For uniformness with override + Volume: semantic.NewFloat(section, cfg.Key("volume"), semantic.Precision(6)), + } + override.Map(section) + commodity.Volumes = append(commodity.Volumes, override) + + volume_override_key := cfg.Key("volume_class_override") + for index, _ := range section.ParamMap[volume_override_key] { + override := &Volume{ + ShipClass: semantic.NewInt(section, volume_override_key, semantic.Index(index), semantic.Order(0)), + Volume: semantic.NewFloat(section, volume_override_key, semantic.Precision(6), semantic.OptsF(semantic.Index(index), semantic.Order(1))), + } + override.Map(section) + commodity.Volumes = append(commodity.Volumes, override) + } + + frelconfig.Commodities = append(frelconfig.Commodities, commodity) + frelconfig.CommoditiesMap[commodity.Nickname.Get()] = commodity + case "[gun]": + gun := &Gun{ + FlashParticleName: semantic.NewString(section, cfg.Key("flash_particle_name"), semantic.WithLowercaseS(), semantic.WithoutSpacesS()), + DispersionAngle: semantic.NewFloat(section, cfg.Key("dispersion_angle"), semantic.Precision(2)), + Volume: semantic.NewFloat(section, cfg.Key("volume"), semantic.Precision(2)), + + BurstAmmo: semantic.NewInt(section, cfg.Key("burst_fire")), + BurstReload: semantic.NewFloat(section, cfg.Key("burst_fire"), semantic.Precision(2), semantic.OptsF(semantic.Order(1))), + NumBarrels: semantic.NewInt(section, cfg.Key("num_barrels")), + Mass: semantic.NewFloat(section, cfg.Key("mass"), semantic.Precision(2)), + } + gun.Map(section) + + gun.Nickname = semantic.NewString(section, cfg.Key("nickname"), semantic.WithLowercaseS(), semantic.WithoutSpacesS()) + gun.IdsName = semantic.NewInt(section, cfg.Key("ids_name"), semantic.Optional()) + gun.IdsInfo = semantic.NewInt(section, cfg.Key("ids_info"), semantic.Optional()) + gun.HitPts = semantic.NewString(section, cfg.Key("hit_pts")) + gun.PowerUsage = semantic.NewFloat(section, cfg.Key("power_usage"), semantic.Precision(2)) + gun.RefireDelay = semantic.NewFloat(section, cfg.Key("refire_delay"), semantic.Precision(2)) + gun.MuzzleVelosity = semantic.NewFloat(section, cfg.Key("muzzle_velocity"), semantic.Precision(2)) + gun.Toughness = semantic.NewFloat(section, cfg.Key("toughness"), semantic.Precision(2)) + gun.IsAutoTurret = semantic.NewBool(section, cfg.Key("auto_turret"), semantic.StrBool) + gun.TurnRate = semantic.NewFloat(section, cfg.Key("turn_rate"), semantic.Precision(2)) + gun.ProjectileArchetype = semantic.NewString(section, cfg.Key("projectile_archetype"), semantic.WithLowercaseS(), semantic.WithoutSpacesS()) + gun.HPGunType = semantic.NewString(section, cfg.Key("hp_gun_type")) + gun.Lootable = semantic.NewBool(section, cfg.Key("lootable"), semantic.StrBool) + frelconfig.Guns = append(frelconfig.Guns, gun) + frelconfig.GunMap[gun.Nickname.Get()] = gun + case "[munition]": + munition := &Munition{ + ConstEffect: semantic.NewString(section, cfg.Key("const_effect"), semantic.WithLowercaseS(), semantic.WithoutSpacesS()), + MunitionHitEffect: semantic.NewString(section, cfg.Key("munition_hit_effect"), semantic.WithLowercaseS(), semantic.WithoutSpacesS()), + + SeekerType: semantic.NewString(section, cfg.Key("seeker")), + SeekerRange: semantic.NewInt(section, cfg.Key("seeker_range")), + SeekerFovDeg: semantic.NewInt(section, cfg.Key("seeker_fov_deg")), + + ArmorPen: semantic.NewFloat(section, cfg.Key("armor_pen"), semantic.Precision(2), semantic.WithDefaultF(0)), + Mass: semantic.NewFloat(section, cfg.Key("mass"), semantic.Precision(2)), + } + munition.Map(section) + munition.Nickname = semantic.NewString(section, cfg.Key("nickname"), semantic.WithLowercaseS(), semantic.WithoutSpacesS()) + munition.IdsName = semantic.NewInt(section, cfg.Key("ids_name"), semantic.Optional()) + munition.IdsInfo = semantic.NewInt(section, cfg.Key("ids_info"), semantic.Optional()) + munition.ExplosionArch = semantic.NewString(section, cfg.Key("explosion_arch")) + munition.RequiredAmmo = semantic.NewBool(section, cfg.Key("requires_ammo"), semantic.StrBool) + munition.HullDamage = semantic.NewInt(section, cfg.Key("hull_damage")) + munition.EnergyDamange = semantic.NewInt(section, cfg.Key("energy_damage")) + munition.HealintAmount = semantic.NewInt(section, cfg.Key("damage")) + munition.WeaponType = semantic.NewString(section, cfg.Key("weapon_type"), semantic.WithLowercaseS(), semantic.WithoutSpacesS()) + munition.LifeTime = semantic.NewFloat(section, cfg.Key("lifetime"), semantic.Precision(2)) + munition.Motor = semantic.NewString(section, cfg.Key("motor"), semantic.WithLowercaseS(), semantic.WithoutSpacesS()) + munition.MaxAngularVelocity = semantic.NewFloat(section, cfg.Key("max_angular_velocity"), semantic.Precision(4)) + + munition.HitPts = semantic.NewInt(section, cfg.Key("hit_pts")) + munition.AmmoLimitAmountInCatridge = semantic.NewInt(section, cfg.Key("ammo_limit")) + munition.AmmoLimitMaxCatridges = semantic.NewInt(section, cfg.Key("ammo_limit"), semantic.Order(1)) + munition.Volume = semantic.NewFloat(section, cfg.Key("volume"), semantic.Precision(4)) + + frelconfig.Munitions = append(frelconfig.Munitions, munition) + frelconfig.MunitionMap[munition.Nickname.Get()] = munition + case "[explosion]": + explosion := &Explosion{ + ArmorPen: semantic.NewFloat(section, cfg.Key("armor_pen"), semantic.Precision(2), semantic.WithDefaultF(0)), + } + explosion.Nickname = semantic.NewString(section, cfg.Key("nickname"), semantic.WithLowercaseS(), semantic.WithoutSpacesS()) + explosion.HullDamage = semantic.NewInt(section, cfg.Key("hull_damage")) + explosion.EnergyDamange = semantic.NewInt(section, cfg.Key("energy_damage")) + explosion.Radius = semantic.NewInt(section, cfg.Key("radius")) + frelconfig.Explosions = append(frelconfig.Explosions, explosion) + frelconfig.ExplosionMap[explosion.Nickname.Get()] = explosion + case "[minedropper]": + mine_dropper := &MineDropper{ + Nickname: semantic.NewString(section, cfg.Key("nickname"), semantic.WithLowercaseS(), semantic.WithoutSpacesS()), + IdsName: semantic.NewInt(section, cfg.Key("ids_name"), semantic.Optional()), + IdsInfo: semantic.NewInt(section, cfg.Key("ids_info"), semantic.Optional()), + HitPts: semantic.NewInt(section, cfg.Key("hit_pts")), + ChildImpulse: semantic.NewFloat(section, cfg.Key("child_impulse"), semantic.Precision(2)), + PowerUsage: semantic.NewFloat(section, cfg.Key("power_usage"), semantic.Precision(2)), + RefireDelay: semantic.NewFloat(section, cfg.Key("refire_delay"), semantic.Precision(2)), + MuzzleVelocity: semantic.NewFloat(section, cfg.Key("muzzle_velocity"), semantic.Precision(2)), + Toughness: semantic.NewFloat(section, cfg.Key("toughness"), semantic.Precision(2)), + ProjectileArchetype: semantic.NewString(section, cfg.Key("projectile_archetype"), semantic.WithLowercaseS(), semantic.WithoutSpacesS()), + Lootable: semantic.NewBool(section, cfg.Key("lootable"), semantic.StrBool), + Mass: semantic.NewFloat(section, cfg.Key("mass"), semantic.Precision(2)), + } + + frelconfig.MineDroppers = append(frelconfig.MineDroppers, mine_dropper) + case "[mine]": + mine := &Mine{ + Nickname: semantic.NewString(section, cfg.Key("nickname"), semantic.WithLowercaseS(), semantic.WithoutSpacesS()), + ExplosionArch: semantic.NewString(section, cfg.Key("explosion_arch"), semantic.WithLowercaseS(), semantic.WithoutSpacesS()), + AmmoLimitAmountInCatridge: semantic.NewInt(section, cfg.Key("ammo_limit")), + AmmoLimitMaxCatridges: semantic.NewInt(section, cfg.Key("ammo_limit"), semantic.Order(1)), + + HitPts: semantic.NewInt(section, cfg.Key("hit_pts")), + Lifetime: semantic.NewFloat(section, cfg.Key("lifetime"), semantic.Precision(2)), + IdsName: semantic.NewInt(section, cfg.Key("ids_name"), semantic.Optional()), + IdsInfo: semantic.NewInt(section, cfg.Key("ids_info"), semantic.Optional()), + SeekDist: semantic.NewInt(section, cfg.Key("seek_dist")), + TopSpeed: semantic.NewInt(section, cfg.Key("top_speed")), + Acceleration: semantic.NewInt(section, cfg.Key("acceleration")), + OwnerSafeTime: semantic.NewInt(section, cfg.Key("owner_safe_time")), + DetonationDistance: semantic.NewInt(section, cfg.Key("detonation_dist")), + LinearDrag: semantic.NewFloat(section, cfg.Key("linear_drag"), semantic.Precision(6)), + Mass: semantic.NewFloat(section, cfg.Key("mass"), semantic.Precision(2)), + } + frelconfig.Mines = append(frelconfig.Mines, mine) + frelconfig.MinesMap[mine.Nickname.Get()] = mine + case "[shieldgenerator]": + shield := &ShieldGenerator{ + Nickname: semantic.NewString(section, cfg.Key("nickname"), semantic.WithLowercaseS(), semantic.WithoutSpacesS()), + IdsName: semantic.NewInt(section, cfg.Key("ids_name"), semantic.Optional()), + IdsInfo: semantic.NewInt(section, cfg.Key("ids_info"), semantic.Optional()), + HitPts: semantic.NewInt(section, cfg.Key("hit_pts")), + Volume: semantic.NewInt(section, cfg.Key("volume")), + RegenerationRate: semantic.NewInt(section, cfg.Key("regeneration_rate")), + MaxCapacity: semantic.NewInt(section, cfg.Key("max_capacity")), + Toughness: semantic.NewFloat(section, cfg.Key("toughness"), semantic.Precision(2)), + HpType: semantic.NewString(section, cfg.Key("hp_type"), semantic.WithLowercaseS(), semantic.WithoutSpacesS()), + ConstPowerDraw: semantic.NewInt(section, cfg.Key("constant_power_draw")), + RebuildPowerDraw: semantic.NewInt(section, cfg.Key("rebuild_power_draw")), + OfflineRebuildTime: semantic.NewInt(section, cfg.Key("offline_rebuild_time")), + Lootable: semantic.NewBool(section, cfg.Key("lootable"), semantic.StrBool), + ShieldType: semantic.NewString(section, cfg.Key("shield_type"), semantic.WithLowercaseS(), semantic.WithoutSpacesS()), + Mass: semantic.NewFloat(section, cfg.Key("mass"), semantic.Precision(2)), + } + frelconfig.ShieldGens = append(frelconfig.ShieldGens, shield) + frelconfig.ShidGenMap[shield.Nickname.Get()] = shield + case "[cloakingdevice]": + cloak := &CloakingDevice{ + Nickname: semantic.NewString(section, cfg.Key("nickname"), semantic.WithLowercaseS(), semantic.WithoutSpacesS()), + IdsName: semantic.NewInt(section, cfg.Key("ids_name"), semantic.Optional()), + IdsInfo: semantic.NewInt(section, cfg.Key("ids_info"), semantic.Optional()), + HitPts: semantic.NewInt(section, cfg.Key("hit_pts")), + PowerUsage: semantic.NewFloat(section, cfg.Key("power_usage"), semantic.Precision(2)), + Volume: semantic.NewFloat(section, cfg.Key("volume"), semantic.Precision(2)), + CloakInTime: semantic.NewInt(section, cfg.Key("cloakin_time")), + CloakOutTime: semantic.NewInt(section, cfg.Key("cloakout_time")), + } + frelconfig.Cloaks = append(frelconfig.Cloaks, cloak) + case "[thruster]": + thruster := &Thruster{ + Nickname: semantic.NewString(section, cfg.Key("nickname"), semantic.WithLowercaseS(), semantic.WithoutSpacesS()), + IdsName: semantic.NewInt(section, cfg.Key("ids_name"), semantic.Optional()), + IdsInfo: semantic.NewInt(section, cfg.Key("ids_info"), semantic.Optional()), + HitPts: semantic.NewInt(section, cfg.Key("hit_pts")), + Lootable: semantic.NewBool(section, cfg.Key("lootable"), semantic.StrBool), + MaxForce: semantic.NewInt(section, cfg.Key("max_force")), + PowerUsage: semantic.NewInt(section, cfg.Key("power_usage")), + Mass: semantic.NewFloat(section, cfg.Key("mass"), semantic.Precision(2)), + } + frelconfig.Thrusters = append(frelconfig.Thrusters, thruster) + frelconfig.ThrusterMap[thruster.Nickname.Get()] = thruster + case "[power]": + power := &Power{ + Nickname: semantic.NewString(section, cfg.Key("nickname"), semantic.WithLowercaseS(), semantic.WithoutSpacesS()), + IdsName: semantic.NewInt(section, cfg.Key("ids_name"), semantic.Optional()), + IdsInfo: semantic.NewInt(section, cfg.Key("ids_info"), semantic.Optional()), + Capacity: semantic.NewInt(section, cfg.Key("capacity")), + ChargeRate: semantic.NewInt(section, cfg.Key("charge_rate")), + ThrustCapacity: semantic.NewInt(section, cfg.Key("thrust_capacity")), + ThrustRecharge: semantic.NewInt(section, cfg.Key("thrust_charge_rate")), + Mass: semantic.NewFloat(section, cfg.Key("mass"), semantic.Precision(2)), + } + frelconfig.Powers = append(frelconfig.Powers, power) + frelconfig.PowersMap[power.Nickname.Get()] = power + case "[engine]": + engine := &Engine{ + Nickname: semantic.NewString(section, cfg.Key("nickname"), semantic.WithLowercaseS(), semantic.WithoutSpacesS()), + IdsName: semantic.NewInt(section, cfg.Key("ids_name"), semantic.Optional()), + IdsInfo: semantic.NewInt(section, cfg.Key("ids_info"), semantic.Optional()), + CruiseSpeed: semantic.NewInt(section, cfg.Key("cruise_speed")), + LinearDrag: semantic.NewInt(section, cfg.Key("linear_drag")), + MaxForce: semantic.NewInt(section, cfg.Key("max_force")), + ReverseFraction: semantic.NewFloat(section, cfg.Key("reverse_fraction"), semantic.Precision(2)), + + HpType: semantic.NewString(section, cfg.Key("hp_type"), semantic.WithLowercaseS(), semantic.WithoutSpacesS()), + FlameEffect: semantic.NewString(section, cfg.Key("flame_effect"), semantic.WithLowercaseS(), semantic.WithoutSpacesS()), + TrailEffect: semantic.NewString(section, cfg.Key("trail_effect"), semantic.WithLowercaseS(), semantic.WithoutSpacesS()), + CruiseChargeTime: semantic.NewInt(section, cfg.Key("cruise_charge_time")), + Mass: semantic.NewFloat(section, cfg.Key("mass"), semantic.Precision(2)), + } + frelconfig.Engines = append(frelconfig.Engines, engine) + frelconfig.EnginesMap[engine.Nickname.Get()] = engine + case "[tractor]": + tractor := &Tractor{ + Nickname: semantic.NewString(section, cfg.Key("nickname"), semantic.WithLowercaseS(), semantic.WithoutSpacesS()), + IdsName: semantic.NewInt(section, cfg.Key("ids_name"), semantic.Optional()), + IdsInfo: semantic.NewInt(section, cfg.Key("ids_info"), semantic.Optional()), + MaxLength: semantic.NewInt(section, cfg.Key("max_length")), + ReachSpeed: semantic.NewInt(section, cfg.Key("reach_speed")), + Lootable: semantic.NewBool(section, cfg.Key("lootable"), semantic.StrBool), + Mass: semantic.NewFloat(section, cfg.Key("mass"), semantic.Precision(2)), + } + frelconfig.Tractors = append(frelconfig.Tractors, tractor) + case "[countermeasuredropper]": + item := &CounterMeasureDropper{ + Nickname: semantic.NewString(section, cfg.Key("nickname"), semantic.WithLowercaseS(), semantic.WithoutSpacesS()), + IdsName: semantic.NewInt(section, cfg.Key("ids_name"), semantic.Optional()), + IdsInfo: semantic.NewInt(section, cfg.Key("ids_info"), semantic.Optional()), + Lootable: semantic.NewBool(section, cfg.Key("lootable"), semantic.StrBool), + + ProjectileArchetype: semantic.NewString(section, cfg.Key("projectile_archetype"), semantic.WithLowercaseS(), semantic.WithoutSpacesS()), + HitPts: semantic.NewInt(section, cfg.Key("hit_pts")), + AIRange: semantic.NewInt(section, cfg.Key("ai_range")), + Mass: semantic.NewFloat(section, cfg.Key("mass"), semantic.Precision(2)), + } + frelconfig.CounterMeasureDroppers = append(frelconfig.CounterMeasureDroppers, item) + case "[countermeasure]": + item := &CounterMeasure{ + Nickname: semantic.NewString(section, cfg.Key("nickname"), semantic.WithLowercaseS(), semantic.WithoutSpacesS()), + IdsName: semantic.NewInt(section, cfg.Key("ids_name"), semantic.Optional()), + IdsInfo: semantic.NewInt(section, cfg.Key("ids_info"), semantic.Optional()), + + AmmoLimitAmountInCatridge: semantic.NewInt(section, cfg.Key("ammo_limit")), + AmmoLimitMaxCatridges: semantic.NewInt(section, cfg.Key("ammo_limit"), semantic.Order(1)), + Lifetime: semantic.NewInt(section, cfg.Key("lifetime")), + Range: semantic.NewInt(section, cfg.Key("range")), + DiversionPctg: semantic.NewInt(section, cfg.Key("diversion_pctg")), + Mass: semantic.NewFloat(section, cfg.Key("mass"), semantic.Precision(2)), + } + frelconfig.CounterMeasure = append(frelconfig.CounterMeasure, item) + frelconfig.CounterMeasureMap[item.Nickname.Get()] = item + case "[scanner]": + item := &Scanner{ + Nickname: semantic.NewString(section, cfg.Key("nickname"), semantic.WithLowercaseS(), semantic.WithoutSpacesS()), + IdsName: semantic.NewInt(section, cfg.Key("ids_name"), semantic.Optional()), + IdsInfo: semantic.NewInt(section, cfg.Key("ids_info"), semantic.Optional()), + + Range: semantic.NewInt(section, cfg.Key("range")), + CargoScanRange: semantic.NewInt(section, cfg.Key("cargo_scan_range")), + Lootable: semantic.NewBool(section, cfg.Key("lootable"), semantic.StrBool), + Mass: semantic.NewFloat(section, cfg.Key("mass"), semantic.Precision(2)), + } + frelconfig.Scanners = append(frelconfig.Scanners, item) + } + } + } + + return frelconfig +} + +func (frelconfig *Config) Write() []*file.File { + var files []*file.File + for _, file := range frelconfig.Files { + inifile := file.Render() + inifile.Write(inifile.File) + files = append(files, inifile.File) + } + return files +} diff --git a/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/equipment_mapped/equipment.go b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/equipment_mapped/equipment.go new file mode 100644 index 0000000..754ca33 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/equipment_mapped/equipment.go @@ -0,0 +1 @@ +package equipment_mapped diff --git a/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/equipment_mapped/goods.go b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/equipment_mapped/goods.go new file mode 100644 index 0000000..e6b0934 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/equipment_mapped/goods.go @@ -0,0 +1,177 @@ +package equipment_mapped + +import ( + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/filefind/file" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/iniload" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/semantic" + + "github.com/darklab8/fl-darkstat/configs/cfg" + "github.com/darklab8/go-utils/utils/utils_types" +) + +type Commodity struct { + semantic.Model + Nickname *semantic.String + Equipment *semantic.String + Category *semantic.String + + Price *semantic.Int + Combinable *semantic.Bool + GoodSellPrice *semantic.Float + BadBuyPrice *semantic.Float + BadSellPrice *semantic.Float + GoodBuyPrice *semantic.Float + ShopArchetype *semantic.Path + ItemIcon *semantic.Path + JumpDist *semantic.Int +} + +type Ship struct { + semantic.Model + Category *semantic.String + Nickname *semantic.String + Hull *semantic.String + Addons []*Addon +} + +type Addon struct { + semantic.Model + ItemNickname *semantic.String + ItemClass *semantic.String + Quantity *semantic.Int +} +type ShipHull struct { + semantic.Model + Nickname *semantic.String + Category *semantic.String + Ship *semantic.String + Price *semantic.Int + IdsName *semantic.Int +} + +type Good struct { + semantic.Model + Category *semantic.String + Nickname *semantic.String + Price *semantic.Int +} + +type Config struct { + Files []*iniload.IniLoader + + Goods []*Good + GoodsMap map[string]*Good + + Commodities []*Commodity + CommoditiesMap map[string]*Commodity + Ships []*Ship + ShipsMap map[string]*Ship + ShipsMapByHull map[string][]*Ship + ShipHulls []*ShipHull + ShipHullsMap map[string]*ShipHull + ShipHullsMapByShip map[string]*ShipHull +} + +const ( + FILENAME utils_types.FilePath = "goods.ini" +) + +func Read(configs []*iniload.IniLoader) *Config { + frelconfig := &Config{Files: configs} + frelconfig.Commodities = make([]*Commodity, 0, 100) + frelconfig.CommoditiesMap = make(map[string]*Commodity) + frelconfig.Ships = make([]*Ship, 0, 100) + frelconfig.ShipsMap = make(map[string]*Ship) + frelconfig.ShipHulls = make([]*ShipHull, 0, 100) + frelconfig.ShipHullsMap = make(map[string]*ShipHull) + + frelconfig.Goods = make([]*Good, 0, 100) + frelconfig.GoodsMap = make(map[string]*Good) + frelconfig.ShipHullsMapByShip = make(map[string]*ShipHull) + frelconfig.ShipsMapByHull = make(map[string][]*Ship) + + for _, config := range configs { + for _, section := range config.SectionMap["[good]"] { + good := &Good{} + good.Map(section) + good.Nickname = semantic.NewString(section, cfg.Key("nickname"), semantic.WithLowercaseS(), semantic.WithoutSpacesS()) + good.Category = semantic.NewString(section, cfg.Key("category"), semantic.WithLowercaseS(), semantic.WithoutSpacesS()) + good.Price = semantic.NewInt(section, cfg.Key("price"), semantic.Optional()) + frelconfig.Goods = append(frelconfig.Goods, good) + frelconfig.GoodsMap[good.Nickname.Get()] = good + + category := good.Category.Get() + switch category { + case "commodity": + commodity := &Commodity{} + commodity.Map(section) + commodity.Category = semantic.NewString(section, cfg.Key("category"), semantic.WithLowercaseS(), semantic.WithoutSpacesS()) + commodity.Nickname = semantic.NewString(section, cfg.Key("nickname"), semantic.WithLowercaseS(), semantic.WithoutSpacesS()) + commodity.Equipment = semantic.NewString(section, cfg.Key("equipment"), semantic.WithLowercaseS(), semantic.WithoutSpacesS()) + commodity.Price = semantic.NewInt(section, cfg.Key("price")) + commodity.Combinable = semantic.NewBool(section, cfg.Key("combinable"), semantic.StrBool) + commodity.GoodSellPrice = semantic.NewFloat(section, cfg.Key("good_sell_price"), semantic.Precision(2)) + commodity.BadBuyPrice = semantic.NewFloat(section, cfg.Key("bad_buy_price"), semantic.Precision(2)) + commodity.BadSellPrice = semantic.NewFloat(section, cfg.Key("bad_sell_price"), semantic.Precision(2)) + commodity.GoodBuyPrice = semantic.NewFloat(section, cfg.Key("good_buy_price"), semantic.Precision(2)) + commodity.ShopArchetype = semantic.NewPath(section, cfg.Key("shop_archetype")) + commodity.ItemIcon = semantic.NewPath(section, cfg.Key("item_icon")) + commodity.JumpDist = semantic.NewInt(section, cfg.Key("jump_dist")) + + frelconfig.Commodities = append(frelconfig.Commodities, commodity) + frelconfig.CommoditiesMap[commodity.Nickname.Get()] = commodity + case "ship": + ship := &Ship{} + ship.Map(section) + ship.Category = semantic.NewString(section, cfg.Key("category")) + ship.Nickname = semantic.NewString(section, cfg.Key("nickname"), semantic.WithLowercaseS(), semantic.WithoutSpacesS()) + ship.Hull = semantic.NewString(section, cfg.Key("hull")) + + for addon_i, _ := range section.ParamMap[cfg.Key("addon")] { + addon := &Addon{ + ItemNickname: semantic.NewString(section, cfg.Key("addon"), + semantic.OptsS(semantic.Index(addon_i), semantic.Order(0)), + semantic.WithLowercaseS(), semantic.WithoutSpacesS()), + ItemClass: semantic.NewString(section, cfg.Key("addon"), + semantic.OptsS(semantic.Index(addon_i), semantic.Order(1)), + semantic.WithLowercaseS(), semantic.WithoutSpacesS()), + Quantity: semantic.NewInt(section, cfg.Key("addon"), + semantic.Index(addon_i), semantic.Order(2), + ), + } + addon.Map(section) + ship.Addons = append(ship.Addons, addon) + } + + frelconfig.Ships = append(frelconfig.Ships, ship) + frelconfig.ShipsMap[ship.Nickname.Get()] = ship + frelconfig.ShipsMapByHull[ship.Hull.Get()] = append(frelconfig.ShipsMapByHull[ship.Hull.Get()], ship) + case "shiphull": + shiphull := &ShipHull{} + shiphull.Map(section) + shiphull.Category = semantic.NewString(section, cfg.Key("category"), semantic.WithLowercaseS(), semantic.WithoutSpacesS()) + shiphull.Nickname = semantic.NewString(section, cfg.Key("nickname"), semantic.WithLowercaseS(), semantic.WithoutSpacesS()) + shiphull.Ship = semantic.NewString(section, cfg.Key("ship"), semantic.WithLowercaseS(), semantic.WithoutSpacesS()) + shiphull.Price = semantic.NewInt(section, cfg.Key("price")) + shiphull.IdsName = semantic.NewInt(section, cfg.Key("ids_name"), semantic.Optional()) + + frelconfig.ShipHulls = append(frelconfig.ShipHulls, shiphull) + frelconfig.ShipHullsMap[shiphull.Nickname.Get()] = shiphull + frelconfig.ShipHullsMapByShip[shiphull.Ship.Get()] = shiphull + } + + } + } + + return frelconfig +} + +func (frelconfig *Config) Write() []*file.File { + var files []*file.File + for _, file := range frelconfig.Files { + inifile := file.Render() + inifile.Write(inifile.File) + files = append(files, inifile.File) + } + return files +} diff --git a/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/equipment_mapped/market_mapped/market.go b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/equipment_mapped/market_mapped/market.go new file mode 100644 index 0000000..bd446ee --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/equipment_mapped/market_mapped/market.go @@ -0,0 +1,117 @@ +package market_mapped + +import ( + "github.com/darklab8/fl-darkstat/configs/cfg" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/filefind/file" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/iniload" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/semantic" + + "github.com/darklab8/go-utils/utils/utils_types" +) + +// Not implemented. Create SemanticMultiKeyValue +type MarketGood struct { + semantic.Model + Nickname *semantic.String // 0 + + LevelRequired *semantic.Int // 1 + RepRequired *semantic.Float // 2 + BaseSellsIPositiveAndDiscoSellPrice *semantic.Int // 3 + + baseSellsIfAboveZero *semantic.Int // 4 . Base sells if value is above 0 + Display *semantic.Int // 5. + PriceModifier *semantic.Float // 6 +} + +func (m *MarketGood) BaseSells() bool { + return m.BaseSellsIPositiveAndDiscoSellPrice.Get() > 0 && m.baseSellsIfAboveZero.Get() > 0 +} + +type BaseGood struct { + semantic.Model + Base *semantic.String + + MarketGoods []*MarketGood + MarketGoodsMap map[string]*MarketGood +} + +type MarketGoodAtBase struct { + MarketGood *MarketGood + Base cfg.BaseUniNick +} + +type Config struct { + Files []*iniload.IniLoader + + BaseGoods []*BaseGood + BasesPerGood map[string][]*MarketGoodAtBase + GoodsPerBase map[cfg.BaseUniNick]*BaseGood +} + +const ( + FILENAME_SHIPS utils_types.FilePath = "market_ships.ini" + FILENAME_COMMODITIES utils_types.FilePath = "market_commodities.ini" + FILENAME_MISC utils_types.FilePath = "market_misc.ini" + BaseGoodType = "[basegood]" +) + +var ( + KEY_MISSMATCH_SYSTEM_FILE = cfg.Key("missmatched_universe_system_and_file") + KEY_MARKET_GOOD = cfg.Key("marketgood") + KEY_BASE = cfg.Key("base") +) + +func Read(files []*iniload.IniLoader) *Config { + frelconfig := &Config{ + Files: files, + GoodsPerBase: make(map[cfg.BaseUniNick]*BaseGood), + } + frelconfig.BaseGoods = make([]*BaseGood, 0) + frelconfig.BasesPerGood = make(map[string][]*MarketGoodAtBase) + + for _, file := range frelconfig.Files { + + for _, section := range file.Sections { + base_to_add := &BaseGood{ + MarketGoodsMap: make(map[string]*MarketGood), + } + base_to_add.Map(section) + base_to_add.Base = semantic.NewString(section, KEY_BASE, semantic.WithLowercaseS(), semantic.WithoutSpacesS()) + base_nickname := cfg.BaseUniNick(base_to_add.Base.Get()) + + for good_index, _ := range section.ParamMap[KEY_MARKET_GOOD] { + good_to_add := &MarketGood{} + good_to_add.Map(section) + good_to_add.Nickname = semantic.NewString(section, KEY_MARKET_GOOD, semantic.OptsS(semantic.Index(good_index)), semantic.WithLowercaseS(), semantic.WithoutSpacesS()) + good_to_add.LevelRequired = semantic.NewInt(section, KEY_MARKET_GOOD, semantic.Index(good_index), semantic.Order(1)) + good_to_add.RepRequired = semantic.NewFloat(section, KEY_MARKET_GOOD, semantic.Precision(2), semantic.OptsF(semantic.Index(good_index), semantic.Order(2))) + good_to_add.BaseSellsIPositiveAndDiscoSellPrice = semantic.NewInt(section, KEY_MARKET_GOOD, semantic.Index(good_index), semantic.Order(3)) + good_to_add.baseSellsIfAboveZero = semantic.NewInt(section, KEY_MARKET_GOOD, semantic.Index(good_index), semantic.Order(4)) + + good_to_add.PriceModifier = semantic.NewFloat(section, KEY_MARKET_GOOD, semantic.Precision(2), semantic.OptsF(semantic.Index(good_index), semantic.Order(6))) + base_to_add.MarketGoods = append(base_to_add.MarketGoods, good_to_add) + base_to_add.MarketGoodsMap[good_to_add.Nickname.Get()] = good_to_add + + frelconfig.BasesPerGood[good_to_add.Nickname.Get()] = append(frelconfig.BasesPerGood[good_to_add.Nickname.Get()], &MarketGoodAtBase{ + MarketGood: good_to_add, + Base: base_nickname, + }) + } + + frelconfig.BaseGoods = append(frelconfig.BaseGoods, base_to_add) + frelconfig.GoodsPerBase[base_nickname] = base_to_add + } + } + + return frelconfig +} + +func (frelconfig *Config) Write() []*file.File { + var files []*file.File + for _, file := range frelconfig.Files { + inifile := file.Render() + inifile.Write(inifile.File) + files = append(files, inifile.File) + } + return files +} diff --git a/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/equipment_mapped/weaponmoddb/weaponmoddb.go b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/equipment_mapped/weaponmoddb/weaponmoddb.go new file mode 100644 index 0000000..ff276d8 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/equipment_mapped/weaponmoddb/weaponmoddb.go @@ -0,0 +1,64 @@ +package weaponmoddb + +import ( + "github.com/darklab8/fl-darkstat/configs/cfg" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/filefind/file" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/iniload" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/semantic" +) + +const ( + FILENAME = "weaponmoddb.ini" +) + +type ShieldMod struct { + semantic.Model + ShieldType *semantic.String + DamageModifier *semantic.Float +} + +type WeaponType struct { + semantic.Model + Nickname *semantic.String + ShieldMods []*ShieldMod +} + +type Config struct { + *iniload.IniLoader + + WeaponTypes []*WeaponType + WeaponTypesMap map[string]*WeaponType +} + +func Read(input_file *iniload.IniLoader) *Config { + frelconfig := &Config{ + IniLoader: input_file, + WeaponTypesMap: make(map[string]*WeaponType), + } + if sections, ok := frelconfig.SectionMap["[weapontype]"]; ok { + for _, section := range sections { + weapon_type := &WeaponType{} + weapon_type.Map(section) + weapon_type.Nickname = semantic.NewString(section, cfg.Key("nickname"), semantic.WithLowercaseS(), semantic.WithoutSpacesS()) + + KEY_SHIELD_MODE := cfg.Key("shield_mod") + for index, _ := range section.ParamMap[KEY_SHIELD_MODE] { + shield_mode := &ShieldMod{} + shield_mode.Map(section) + shield_mode.ShieldType = semantic.NewString(section, KEY_SHIELD_MODE, semantic.OptsS(semantic.Index(index)), semantic.WithLowercaseS(), semantic.WithoutSpacesS()) + shield_mode.DamageModifier = semantic.NewFloat(section, KEY_SHIELD_MODE, semantic.Precision(2), semantic.OptsF(semantic.Index(index), semantic.Order(1))) + weapon_type.ShieldMods = append(weapon_type.ShieldMods, shield_mode) + } + frelconfig.WeaponTypes = append(frelconfig.WeaponTypes, weapon_type) + frelconfig.WeaponTypesMap[weapon_type.Nickname.Get()] = weapon_type + } + } + + return frelconfig +} + +func (frelconfig *Config) Write() *file.File { + inifile := frelconfig.Render() + inifile.Write(inifile.File) + return inifile.File +} diff --git a/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/initialworld/flhash/flhash_faction.go b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/initialworld/flhash/flhash_faction.go new file mode 100644 index 0000000..cdab1f1 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/initialworld/flhash/flhash_faction.go @@ -0,0 +1,64 @@ +package flhash + +import ( + "strings" +) + +const ( + FLFACTIONHASH_POLYNOMIAL = 0x1021 +) + +type faction_hasher struct { + table [256]uint32 +} + +// Function for calculating the Freelancer data nickname hash. +// Algorithm from flhash.exe by sherlog@t-online.de (2003-06-11) +func (h *faction_hasher) RawHash(data []byte) uint32 { + var hash uint32 = 0xFFFF + for _, b := range data { + hash = (hash >> 8) ^ h.table[byte(hash&0xFF)^b] + } + return hash +} + +// NicknameHasher implements the hashing algorithm used by item, base, etc. nicknames +type FactionNickHasher struct { + faction_hasher +} + +func (h *FactionNickHasher) Hash(name string) HashCode { + bytes := []byte(strings.ToLower(name)) + hash := h.RawHash(bytes) + // hash = (hash >> (physicalBits - logicalBits)) | 0x80000000 + return HashCode(hash) +} + +func NewFactionHasher() *FactionNickHasher { + h := FactionNickHasher{} + NotSimplePopulateTable(FLFACTIONHASH_POLYNOMIAL, &h.table) + return &h +} + +var faction = NewFactionHasher() + +func HashFaction(name string) HashCode { + return faction.Hash(name) +} + +// Copied foundation from golang crc32 that had SimplePopulate +// And then modified to how it should at Python version given by Alex +// Matches also Laz version in C# i think +func NotSimplePopulateTable(poly uint32, t *[256]uint32) { + for i := 0; i < 256; i++ { + crc := uint32(i) << 8 + for j := 0; j < 8; j++ { + crc <<= 1 + + if crc&0x10000 != 0 { + crc = (crc ^ poly) & 0xFFFF + } + } + t[i] = crc + } +} diff --git a/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/initialworld/flhash/flhash_name.go b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/initialworld/flhash/flhash_name.go new file mode 100644 index 0000000..508dfb0 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/initialworld/flhash/flhash_name.go @@ -0,0 +1,39 @@ +package flhash + +import ( + "encoding/binary" + "fmt" + "hash/crc32" + "strings" + "unicode/utf16" +) + +// NameHash implements the hashing algorithm used by the account folders and save files. +type NameHash struct { + hasher +} + +func (h *NameHash) Hash(name string) uint32 { + codes := utf16.Encode([]rune(strings.ToLower(name))) + bytes := make([]byte, 2*len(codes)) + for i, c := range codes { + binary.LittleEndian.PutUint16(bytes[2*i:2*(i+1)], c) + } + return h.RawHash(bytes) +} + +func (h *NameHash) SaveFile(name string) string { + return fmt.Sprintf("%02x-%08x", len(name), h.Hash(name)) +} + +func NewNameHasher() *NameHash { + h := NameHash{} + h.table = *crc32.MakeTable(flNameHashPolynomial) + return &h +} + +var n = NewNameHasher() + +func SaveFile(name string) string { + return n.SaveFile(name) +} diff --git a/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/initialworld/flhash/flhash_nick.go b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/initialworld/flhash/flhash_nick.go new file mode 100644 index 0000000..228258d --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/initialworld/flhash/flhash_nick.go @@ -0,0 +1,68 @@ +package flhash + +import ( + "fmt" + "hash/crc32" + "strconv" + "strings" +) + +const ( + logicalBits = 30 + physicalBits = 32 + flHashPolynomial uint32 = 0xA001 << (logicalBits - 16) + flNameHashPolynomial uint32 = 0x50008 << (physicalBits - 20) +) + +type hasher struct { + table [256]uint32 +} + +// Function for calculating the Freelancer data nickname hash. +// Algorithm from flhash.exe by sherlog@t-online.de (2003-06-11) +func (h *hasher) RawHash(data []byte) uint32 { + var hash uint32 + for _, b := range data { + hash = (hash >> 8) ^ h.table[byte(hash)^b] + } + hash = (hash >> 24) | ((hash >> 8) & 0x0000FF00) | ((hash << 8) & 0x00FF0000) | (hash << 24) + return hash +} + +// NicknameHasher implements the hashing algorithm used by item, base, etc. nicknames +type NicknameHasher struct { + hasher +} + +type HashCode int + +func (h HashCode) ToIntStr() string { + return fmt.Sprintf("%d", int32(h)) +} + +func (h HashCode) ToUintStr() string { + return strconv.FormatUint(uint64(int(h)), 10) +} + +func (h HashCode) ToHexStr() string { + return fmt.Sprintf("%x", int(h)) +} + +func (h *NicknameHasher) Hash(name string) HashCode { + bytes := []byte(strings.ToLower(name)) + hash := h.RawHash(bytes) + hash = (hash >> (physicalBits - logicalBits)) | 0x80000000 + return HashCode(hash) +} + +func NewHasher() *NicknameHasher { + h := NicknameHasher{} + h.table = *crc32.MakeTable(flHashPolynomial) + return &h +} + +var nick = NewHasher() + +func HashNickname(name string) HashCode { + return nick.Hash(name) +} diff --git a/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/initialworld/initialworld.go b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/initialworld/initialworld.go new file mode 100644 index 0000000..8b6a7f7 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/initialworld/initialworld.go @@ -0,0 +1,99 @@ +package initialworld + +import ( + "strconv" + + "github.com/darklab8/fl-darkstat/configs/cfg" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/initialworld/flhash" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/filefind/file" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/iniload" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/semantic" + "github.com/darklab8/fl-darkstat/configs/configs_settings/logus" +) + +const ( + FILENAME = "initialworld.ini" +) + +type Relationship struct { + semantic.Model + + Rep *semantic.Float + TargetNickname *semantic.String +} + +type Group struct { + semantic.Model + + Nickname *semantic.String + IdsName *semantic.Int + IdsInfo *semantic.Int + + IdsShortName *semantic.Int + Relationships []*Relationship +} + +type Config struct { + *iniload.IniLoader + + Groups []*Group + GroupsMap map[string]*Group + + LockedGates map[flhash.HashCode]bool +} + +func Read(input_file *iniload.IniLoader) *Config { + config := &Config{ + IniLoader: input_file, + Groups: make([]*Group, 0, 100), + GroupsMap: make(map[string]*Group), + LockedGates: make(map[flhash.HashCode]bool), + } + + if locked_gates, ok := config.SectionMap["[locked_gates]"]; ok { + + for _, values := range locked_gates[0].ParamMap[cfg.Key("locked_gate")] { + + int_hash, err := strconv.Atoi(values.First.AsString()) + + logus.Log.CheckPanic(err, "failed to parse locked_gate") + + config.LockedGates[flhash.HashCode(int_hash)] = true + } + } + + if groups, ok := config.SectionMap["[group]"]; ok { + + for _, group_res := range groups { + group := &Group{} + group.Map(group_res) + group.Nickname = semantic.NewString(group_res, cfg.Key("nickname"), semantic.WithLowercaseS(), semantic.WithoutSpacesS()) + group.IdsName = semantic.NewInt(group_res, cfg.Key("ids_name"), semantic.Optional()) + group.IdsInfo = semantic.NewInt(group_res, cfg.Key("ids_info"), semantic.Optional()) + group.IdsShortName = semantic.NewInt(group_res, cfg.Key("ids_short_name")) + + group.Relationships = make([]*Relationship, 0, 20) + + param_rep_key := cfg.Key("rep") + for rep_index, _ := range group_res.ParamMap[param_rep_key] { + + rep := &Relationship{} + rep.Map(group_res) + rep.Rep = semantic.NewFloat(group_res, param_rep_key, semantic.Precision(2), semantic.OptsF(semantic.Index(rep_index))) + rep.TargetNickname = semantic.NewString(group_res, param_rep_key, semantic.OptsS(semantic.Index(rep_index), semantic.Order(1)), semantic.WithLowercaseS(), semantic.WithoutSpacesS()) + group.Relationships = append(group.Relationships, rep) + } + + config.Groups = append(config.Groups, group) + config.GroupsMap[group.Nickname.Get()] = group + } + } + + return config +} + +func (frelconfig *Config) Write() *file.File { + inifile := frelconfig.Render() + inifile.Write(inifile.File) + return inifile.File +} diff --git a/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/interface_mapped/interface.go b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/interface_mapped/interface.go new file mode 100644 index 0000000..2ba8492 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/interface_mapped/interface.go @@ -0,0 +1,57 @@ +package interface_mapped + +import ( + "strconv" + + "github.com/darklab8/fl-darkstat/configs/cfg" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/filefind/file" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/iniload" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/inireader/inireader_types" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/semantic" + "github.com/darklab8/fl-darkstat/configs/configs_settings/logus" +) + +const ( + FILENAME_FL_INI = "infocardmap.ini" + RESOURCE_HEADER_MAP_TABLE inireader_types.IniHeader = "[infocardmaptable]" + RESOURCE_KEY_MAP = "map" +) + +type InfocardMapTable struct { + semantic.Model + Map map[int]int +} + +type Config struct { + *iniload.IniLoader + InfocardMapTable InfocardMapTable +} + +func Read(input_file *iniload.IniLoader) *Config { + frelconfig := &Config{ + IniLoader: input_file, + InfocardMapTable: InfocardMapTable{Map: make(map[int]int)}, + } + + if resources, ok := input_file.SectionMap[RESOURCE_HEADER_MAP_TABLE]; ok { + + for _, mappy := range resources[0].ParamMap[cfg.Key("map")] { + + id_key, err := strconv.Atoi(mappy.First.AsString()) + logus.Log.CheckPanic(err, "failed to read number from infocardmaptable") + + id_value, err := strconv.Atoi(mappy.Values[1].AsString()) + logus.Log.CheckPanic(err, "failed to read number from infocardmaptable") + + frelconfig.InfocardMapTable.Map[id_key] = id_value + } + } + + return frelconfig +} + +func (frelconfig *Config) Write() *file.File { + inifile := frelconfig.Render() + inifile.Write(inifile.File) + return inifile.File +} diff --git a/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/missions_mapped/empathy_mapped/empathy.go b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/missions_mapped/empathy_mapped/empathy.go new file mode 100644 index 0000000..7676129 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/missions_mapped/empathy_mapped/empathy.go @@ -0,0 +1,89 @@ +package empathy_mapped + +import ( + "github.com/darklab8/fl-darkstat/configs/cfg" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/filefind/file" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/iniload" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/semantic" + + "github.com/darklab8/go-utils/utils/utils_types" +) + +type EmpathyRate struct { + semantic.Model + + TargetFactionNickname *semantic.String // 0 + RepoChange *semantic.Float // 1 +} + +type RepChangeEffects struct { + semantic.Model + Group *semantic.String + + ObjectDestruction *semantic.Float + MissionSuccess *semantic.Float + MissionFailure *semantic.Float + MissionAbort *semantic.Float + + EmpathyRates []*EmpathyRate + EmpathyRatesMap map[string]*EmpathyRate +} + +type Config struct { + *iniload.IniLoader + RepChangeEffects []*RepChangeEffects + RepoChangeMap map[string]*RepChangeEffects +} + +const ( + FILENAME utils_types.FilePath = "empathy.ini" +) + +func Read(input_file *iniload.IniLoader) *Config { + frelconfig := &Config{IniLoader: input_file} + + frelconfig.RepChangeEffects = make([]*RepChangeEffects, 0, 20) + frelconfig.RepoChangeMap = make(map[string]*RepChangeEffects) + + for _, section := range input_file.SectionMap["[repchangeeffects]"] { + repo_changes := &RepChangeEffects{} + repo_changes.Map(section) + repo_changes.Group = semantic.NewString(section, cfg.Key("group"), semantic.WithLowercaseS(), semantic.WithoutSpacesS()) + repo_changes.EmpathyRatesMap = make(map[string]*EmpathyRate) + + event_key := cfg.Key("event") + for event_index, event := range section.ParamMap[event_key] { + switch event.First.AsString() { + case "object_destruction": + repo_changes.ObjectDestruction = semantic.NewFloat(section, event_key, semantic.Precision(2), semantic.OptsF(semantic.Index(event_index), semantic.Order(1))) + case "random_mission_success": + repo_changes.MissionSuccess = semantic.NewFloat(section, event_key, semantic.Precision(2), semantic.OptsF(semantic.Index(event_index), semantic.Order(1))) + case "random_mission_failure": + repo_changes.MissionFailure = semantic.NewFloat(section, event_key, semantic.Precision(2), semantic.OptsF(semantic.Index(event_index), semantic.Order(1))) + case "random_mission_abortion": + repo_changes.MissionAbort = semantic.NewFloat(section, event_key, semantic.Precision(2), semantic.OptsF(semantic.Index(event_index), semantic.Order(1))) + } + } + + empathy_rate_key := cfg.Key("empathy_rate") + for good_index, _ := range section.ParamMap[empathy_rate_key] { + empathy := &EmpathyRate{} + empathy.Map(section) + empathy.TargetFactionNickname = semantic.NewString(section, empathy_rate_key, semantic.OptsS(semantic.Index(good_index), semantic.Order(0))) + empathy.RepoChange = semantic.NewFloat(section, empathy_rate_key, semantic.Precision(2), semantic.OptsF(semantic.Index(good_index), semantic.Order(1))) + repo_changes.EmpathyRates = append(repo_changes.EmpathyRates, empathy) + repo_changes.EmpathyRatesMap[empathy.TargetFactionNickname.Get()] = empathy + } + + frelconfig.RepChangeEffects = append(frelconfig.RepChangeEffects, repo_changes) + frelconfig.RepoChangeMap[repo_changes.Group.Get()] = repo_changes + } + return frelconfig + +} + +func (frelconfig *Config) Write() *file.File { + inifile := frelconfig.Render() + inifile.Write(inifile.File) + return inifile.File +} diff --git a/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/missions_mapped/faction_props_mapped/faction_props.go b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/missions_mapped/faction_props_mapped/faction_props.go new file mode 100644 index 0000000..7c10b6f --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/missions_mapped/faction_props_mapped/faction_props.go @@ -0,0 +1,55 @@ +package faction_props_mapped + +import ( + "github.com/darklab8/fl-darkstat/configs/cfg" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/filefind/file" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/iniload" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/semantic" +) + +const ( + FILENAME = "faction_prop.ini" +) + +type FactionProp struct { + semantic.Model + Affiliation *semantic.String + NpcShips []*semantic.String +} + +type Config struct { + *iniload.IniLoader + + FactionProps []*FactionProp + FactionPropMapByNickname map[string]*FactionProp +} + +func Read(input_file *iniload.IniLoader) *Config { + frelconfig := &Config{ + IniLoader: input_file, + FactionPropMapByNickname: make(map[string]*FactionProp), + } + if sections, ok := frelconfig.SectionMap["[factionprops]"]; ok { + for _, section := range sections { + faction_prop := &FactionProp{} + faction_prop.Map(section) + faction_prop.Affiliation = semantic.NewString(section, cfg.Key("affiliation"), semantic.WithLowercaseS(), semantic.WithoutSpacesS()) + + KEY_NPC_SHIP := cfg.Key("npc_ship") + for index, _ := range section.ParamMap[KEY_NPC_SHIP] { + faction_prop.NpcShips = append(faction_prop.NpcShips, + semantic.NewString(section, KEY_NPC_SHIP, semantic.OptsS(semantic.Index(index)), semantic.WithLowercaseS(), semantic.WithoutSpacesS())) + } + frelconfig.FactionProps = append(frelconfig.FactionProps, faction_prop) + frelconfig.FactionPropMapByNickname[faction_prop.Affiliation.Get()] = faction_prop + } + } + + return frelconfig +} + +func (frelconfig *Config) Write() *file.File { + inifile := frelconfig.Render() + inifile.Write(inifile.File) + return inifile.File +} diff --git a/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/missions_mapped/mbases_mapped/mbases.go b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/missions_mapped/mbases_mapped/mbases.go new file mode 100644 index 0000000..99d3541 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/missions_mapped/mbases_mapped/mbases.go @@ -0,0 +1,271 @@ +package mbases_mapped + +import ( + "github.com/darklab8/fl-darkstat/configs/cfg" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/filefind/file" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/iniload" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/semantic" + "github.com/darklab8/fl-darkstat/configs/configs_settings/logus" +) + +const ( + FILENAME = "mbases.ini" +) + +type Mroom struct { + semantic.Model + Nickname *semantic.String + CharacterDensity *semantic.Int + Bartrender *semantic.String +} + +type MissionType struct { + semantic.Model + MinDifficulty *semantic.Float + MaxDifficulty *semantic.Float + Weight *semantic.Int +} + +type BaseFaction struct { + semantic.Model + + MissionType *MissionType + Faction *semantic.String + Weight *semantic.Int + Npcs []*semantic.String +} + +type Bribe struct { + semantic.Model + Faction *semantic.String +} +type Rumor struct { + semantic.Model +} +type Mission struct { + semantic.Model +} +type Know struct { + semantic.Model +} + +type NPC struct { + semantic.Model + + Nickname *semantic.String + Room *semantic.String + Bribes []*Bribe + Rumors []*Rumor + Missions []*Mission + Knows []*Know + Affiliation *semantic.String +} + +type MVendor struct { + semantic.Model + MinOffers *semantic.Int + MaxOffers *semantic.Int +} + +type Base struct { + semantic.Model + + Nickname *semantic.String + LocalFaction *semantic.String + Diff *semantic.Int + + BaseFactions []*BaseFaction + BaseFactionsMap map[string]*BaseFaction + NPCs []*NPC + Bar *Mroom + MVendor *MVendor +} + +type Config struct { + semantic.ConfigModel + + File *iniload.IniLoader + Bases []*Base + BaseMap map[cfg.BaseUniNick]*Base +} + +func Read(input_file *iniload.IniLoader) *Config { + frelconfig := &Config{ + File: input_file, + Bases: make([]*Base, 0, 100), + BaseMap: make(map[cfg.BaseUniNick]*Base), + } + + for i := 0; i < len(input_file.Sections); i++ { + + if input_file.Sections[i].Type == "[mbase]" { + + mbase_section := input_file.Sections[i] + base := &Base{ + BaseFactionsMap: make(map[string]*BaseFaction), + } + base.Map(mbase_section) + base.Nickname = semantic.NewString(mbase_section, cfg.Key("nickname"), semantic.WithLowercaseS(), semantic.WithoutSpacesS()) + base.LocalFaction = semantic.NewString(mbase_section, cfg.Key("local_faction")) + base.Diff = semantic.NewInt(mbase_section, cfg.Key("diff")) + frelconfig.Bases = append(frelconfig.Bases, base) + frelconfig.BaseMap[cfg.BaseUniNick(base.Nickname.Get())] = base + + for j := i + 1; j < len(input_file.Sections) && input_file.Sections[j].Type != "[mbase]"; j++ { + section := input_file.Sections[j] + + switch section.Type { + case "[mvendor]": + vendor := &MVendor{ + MinOffers: semantic.NewInt(section, cfg.Key("num_offers"), semantic.Order(0)), + MaxOffers: semantic.NewInt(section, cfg.Key("num_offers"), semantic.Order(1)), + } + base.MVendor = vendor + case "[basefaction]": + faction := &BaseFaction{ + Faction: semantic.NewString(section, cfg.Key("faction"), semantic.WithLowercaseS(), semantic.WithoutSpacesS()), + Weight: semantic.NewInt(section, cfg.Key("weight")), + } + faction.Map(section) + + mission_type := &MissionType{ + MinDifficulty: semantic.NewFloat(section, cfg.Key("mission_type"), semantic.Precision(2), semantic.OptsF(semantic.Order(1))), + MaxDifficulty: semantic.NewFloat(section, cfg.Key("mission_type"), semantic.Precision(2), semantic.OptsF(semantic.Order(2))), + Weight: semantic.NewInt(section, cfg.Key("mission_type"), semantic.Order(3)), + } + mission_type.Map(section) + faction.MissionType = mission_type + + for index, _ := range section.ParamMap[cfg.Key("npc")] { + faction.Npcs = append(faction.Npcs, + semantic.NewString(mbase_section, cfg.Key("weight"), semantic.OptsS(semantic.Index(index)))) + } + base.BaseFactions = append(base.BaseFactions, faction) + base.BaseFactionsMap[faction.Faction.Get()] = faction + case "[mroom]": + mroom := &Mroom{ + Nickname: semantic.NewString(section, cfg.Key("nickname"), semantic.WithLowercaseS(), semantic.WithoutSpacesS()), + CharacterDensity: semantic.NewInt(section, cfg.Key("character_density")), + Bartrender: semantic.NewString(section, cfg.Key("fixture"), semantic.OptsS(semantic.Order(0), semantic.Optional())), + } + mroom.Map(section) + if mroom.Nickname.Get() == "bar" { + base.Bar = mroom + } + case "[gf_npc]": + npc := &NPC{ + Nickname: semantic.NewString(section, cfg.Key("nickname"), semantic.WithLowercaseS(), semantic.WithoutSpacesS()), + Room: semantic.NewString(section, cfg.Key("room"), semantic.OptsS(semantic.Optional())), + Affiliation: semantic.NewString(section, cfg.Key("affiliation"), semantic.WithLowercaseS(), semantic.WithoutSpacesS()), + } + npc.Map(section) + + for index, _ := range section.ParamMap[cfg.Key("bribe")] { + bribe := &Bribe{ + Faction: semantic.NewString(section, cfg.Key("bribe"), semantic.OptsS(semantic.Index(index)), semantic.WithLowercaseS(), semantic.WithoutSpacesS()), + } + bribe.Map(section) + npc.Bribes = append(npc.Bribes, bribe) + } + for range section.ParamMap[cfg.Key("rumor")] { + rumor := &Rumor{} + rumor.Map(section) + npc.Rumors = append(npc.Rumors, rumor) + } + for range section.ParamMap[cfg.Key("misc")] { + misn := &Mission{} + misn.Map(section) + npc.Missions = append(npc.Missions, misn) + } + for range section.ParamMap[cfg.Key("know")] { + know := &Know{} + know.Map(section) + npc.Knows = append(npc.Knows, know) + } + + base.NPCs = append(base.NPCs, npc) + } + + } + } + + } + + return frelconfig +} + +func (frelconfig *Config) Write() *file.File { + // TODO BEWARE A BUG to fix. + // if having here frelconfig.Render() + // everything is still correct as typing + // but the file is not getting written in darklint + // This bug may be is going through my other code + inifile := frelconfig.File.Render() + inifile.Write(inifile.File) + return inifile.File +} + +type BaseChance struct { + Base string + Chance float64 +} + +func FactionBribes(config *Config) map[string]map[string]float64 { + // for faction, chance at certain base + var faction_rephacks map[string]map[string]float64 = make(map[string]map[string]float64) + + for _, base := range config.Bases { + + // per faction chance at base + logus.Log.Debug("base=" + base.Nickname.Get()) + var base_bribe_chances map[string]float64 = make(map[string]float64) + var faction_members map[string]int = make(map[string]int) + for _, npc := range base.NPCs { + faction_members[npc.Affiliation.Get()] += 1 + + } + for _, npc := range base.NPCs { + if base.Bar == nil { + continue + } + npc_nickname := npc.Nickname.Get() + bartrender := base.Bar.Bartrender.Get() + if npc_nickname == bartrender { + for _, bribe := range npc.Bribes { + chance_increase := 1 / float64(len(npc.Bribes)+len(npc.Rumors)+len(npc.Missions)+len(npc.Knows)) + base_bribe_chances[bribe.Faction.Get()] += chance_increase + } + } else { + for _, bribe := range npc.Bribes { + var weight float64 = 0 + if faction, ok := base.BaseFactionsMap[npc.Affiliation.Get()]; ok { + weight = float64(faction.Weight.Get()) + + if value, ok := faction_members[npc.Affiliation.Get()]; ok { + if value != 0 { + weight = weight / float64(value) + } + } + } + + chance_increase := float64(weight/100) * 1 / float64(len(npc.Bribes)+len(npc.Rumors)+len(npc.Missions)+len(npc.Knows)) + base_bribe_chances[bribe.Faction.Get()] += chance_increase + } + } + } + + for faction, chance := range base_bribe_chances { + _, ok := faction_rephacks[faction] + if !ok { + faction_rephacks[faction] = make(map[string]float64) + } + faction_rephacks[faction][base.Nickname.Get()] += chance + + if faction_rephacks[faction][base.Nickname.Get()] > 1.0 { + faction_rephacks[faction][base.Nickname.Get()] = 1.0 + } + } + + } + return faction_rephacks +} diff --git a/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/missions_mapped/npc_ships/npc_ships.go b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/missions_mapped/npc_ships/npc_ships.go new file mode 100644 index 0000000..297dde9 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/missions_mapped/npc_ships/npc_ships.go @@ -0,0 +1,62 @@ +package npc_ships + +import ( + "github.com/darklab8/fl-darkstat/configs/cfg" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/filefind/file" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/iniload" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/semantic" +) + +const ( + FILENAME = "npcships.ini" +) + +type NPCShipArch struct { + semantic.Model + Nickname *semantic.String + Level *semantic.String + NpcClass []*semantic.String + Loadout *semantic.String +} + +type Config struct { + *iniload.IniLoader + + NpcShips []*NPCShipArch + NpcShipsByNickname map[string]*NPCShipArch +} + +func Read(input_file *iniload.IniLoader) *Config { + frelconfig := &Config{ + IniLoader: input_file, + NpcShipsByNickname: make(map[string]*NPCShipArch), + } + if sections, ok := frelconfig.SectionMap["[npcshiparch]"]; ok { + for _, section := range sections { + npc_ship_arch := &NPCShipArch{ + Loadout: semantic.NewString(section, cfg.Key("loadout"), semantic.WithLowercaseS(), semantic.WithoutSpacesS()), + } + npc_ship_arch.Map(section) + npc_ship_arch.Nickname = semantic.NewString(section, cfg.Key("nickname"), semantic.WithLowercaseS(), semantic.WithoutSpacesS()) + npc_ship_arch.Level = semantic.NewString(section, cfg.Key("level"), semantic.WithLowercaseS(), semantic.WithoutSpacesS()) + + if npc_class_param, ok := section.ParamMap[cfg.Key("npc_class")]; ok { + for index_order, _ := range npc_class_param[0].Values { + npc_ship_arch.NpcClass = append(npc_ship_arch.NpcClass, + semantic.NewString(section, cfg.Key("npc_class"), semantic.OptsS(semantic.Order(index_order)), semantic.WithLowercaseS(), semantic.WithoutSpacesS())) + } + + } + frelconfig.NpcShips = append(frelconfig.NpcShips, npc_ship_arch) + frelconfig.NpcShipsByNickname[npc_ship_arch.Nickname.Get()] = npc_ship_arch + } + } + + return frelconfig +} + +func (frelconfig *Config) Write() *file.File { + inifile := frelconfig.Render() + inifile.Write(inifile.File) + return inifile.File +} diff --git a/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/rnd_msns_mapped/diff2money/diff2money.go b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/rnd_msns_mapped/diff2money/diff2money.go new file mode 100644 index 0000000..b934167 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/rnd_msns_mapped/diff2money/diff2money.go @@ -0,0 +1,49 @@ +package diff2money + +import ( + "github.com/darklab8/fl-darkstat/configs/cfg" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/filefind/file" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/iniload" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/semantic" + "github.com/darklab8/go-utils/utils/utils_types" +) + +type DiffToMoney struct { + semantic.Model + MinLevel *semantic.Float + MoneyAward *semantic.Int +} + +type Config struct { + *iniload.IniLoader + DiffToMoney []*DiffToMoney +} + +const ( + FILENAME utils_types.FilePath = "diff2money.ini" +) + +func Read(input_file *iniload.IniLoader) *Config { + frelconfig := &Config{IniLoader: input_file} + + for _, section := range input_file.SectionMap["[diff2money]"] { + + for index, _ := range section.ParamMap[cfg.Key("diff2money")] { + diff_to_money := &DiffToMoney{ + MinLevel: semantic.NewFloat(section, cfg.Key("diff2money"), semantic.Precision(2), semantic.OptsF(semantic.Index(index), semantic.Order(0))), + MoneyAward: semantic.NewInt(section, cfg.Key("diff2money"), semantic.Index(index), semantic.Order(1)), + } + diff_to_money.Map(section) + frelconfig.DiffToMoney = append(frelconfig.DiffToMoney, diff_to_money) + } + + } + return frelconfig + +} + +func (frelconfig *Config) Write() *file.File { + inifile := frelconfig.Render() + inifile.Write(inifile.File) + return inifile.File +} diff --git a/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/rnd_msns_mapped/npcranktodiff/npcranktodiff.go b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/rnd_msns_mapped/npcranktodiff/npcranktodiff.go new file mode 100644 index 0000000..652c886 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/rnd_msns_mapped/npcranktodiff/npcranktodiff.go @@ -0,0 +1,56 @@ +package npcranktodiff + +import ( + "github.com/darklab8/fl-darkstat/configs/cfg" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/filefind/file" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/iniload" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/semantic" + "github.com/darklab8/go-utils/utils/utils_types" +) + +type NPCRankToDifficulty struct { + semantic.Model + Rank *semantic.Int + Difficulties []*semantic.Float +} + +type Config struct { + *iniload.IniLoader + NPCRankToDifficulties []*NPCRankToDifficulty +} + +const ( + FILENAME utils_types.FilePath = "npcranktodiff.ini" +) + +func Read(input_file *iniload.IniLoader) *Config { + frelconfig := &Config{IniLoader: input_file} + + for _, section := range input_file.SectionMap["[rankandformationsizetodifficulty]"] { + + for index, values := range section.ParamMap[cfg.Key("npcrank")] { + npc_rank_to_diff := &NPCRankToDifficulty{ + Rank: semantic.NewInt(section, cfg.Key("npcrank"), semantic.Index(index), semantic.Order(0)), + } + + len_of_difficulties := len(values.Values) - 1 + for i := 1; i <= len_of_difficulties; i++ { + npc_rank_to_diff.Difficulties = append(npc_rank_to_diff.Difficulties, + semantic.NewFloat(section, cfg.Key("npcrank"), semantic.Precision(2), semantic.OptsF(semantic.Index(index), semantic.Order(i))), + ) + } + + npc_rank_to_diff.Map(section) + frelconfig.NPCRankToDifficulties = append(frelconfig.NPCRankToDifficulties, npc_rank_to_diff) + } + + } + return frelconfig + +} + +func (frelconfig *Config) Write() *file.File { + inifile := frelconfig.Render() + inifile.Write(inifile.File) + return inifile.File +} diff --git a/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/ship_mapped/shiparch.go b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/ship_mapped/shiparch.go new file mode 100644 index 0000000..110aa0f --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/ship_mapped/shiparch.go @@ -0,0 +1,138 @@ +package ship_mapped + +import ( + "github.com/darklab8/fl-darkstat/configs/cfg" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/filefind/file" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/iniload" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/semantic" +) + +type Ship struct { + semantic.Model + Nickname *semantic.String // matches value `ship` in goods + ShipClass *semantic.Int + Type *semantic.String + IdsName *semantic.Int + IdsInfo *semantic.Int + IdsInfo1 *semantic.Int + IdsInfo2 *semantic.Int + IdsInfo3 *semantic.Int + + Nanobots *semantic.Int + Batteries *semantic.Int + Mass *semantic.Float + LinearDrag *semantic.Float + HoldSize *semantic.Int + HitPts *semantic.Int + NudgeForce *semantic.Float + StrafeForce *semantic.Float + ShieldLink *ShieldLink + HpTypes []*HpType + + SteeringTorque *semantic.Vect + AngularDrag *semantic.Vect + RotationIntertia *semantic.Vect + + /* + Some info in Goods with category shiphull, it has link from [Ship] to hulll + Some is good ship, it has stuff leading to [Power], [Engine], [Scanner] and [ShieldGenerator] + + */ + ArmorMult *semantic.Float // disco only +} + +type HpType struct { + semantic.Model + Nickname *semantic.String + AllowedEquipments []*semantic.String +} + +type ShieldLink struct { + semantic.Model + ShieldClass *semantic.String +} + +type Config struct { + Files []*iniload.IniLoader + + Ships []*Ship + ShipsMap map[string]*Ship +} + +func Read(files []*iniload.IniLoader) *Config { + frelconfig := &Config{Files: files} + frelconfig.Ships = make([]*Ship, 0, 100) + frelconfig.ShipsMap = make(map[string]*Ship) + + for _, Iniconfig := range files { + + for _, section := range Iniconfig.SectionMap["[ship]"] { + ship := &Ship{ + Nickname: semantic.NewString(section, cfg.Key("nickname"), semantic.WithLowercaseS(), semantic.WithoutSpacesS()), + Type: semantic.NewString(section, cfg.Key("type")), + ShipClass: semantic.NewInt(section, cfg.Key("ship_class")), + IdsName: semantic.NewInt(section, cfg.Key("ids_name"), semantic.Optional()), + IdsInfo: semantic.NewInt(section, cfg.Key("ids_info"), semantic.Optional()), + IdsInfo1: semantic.NewInt(section, cfg.Key("ids_info1")), + IdsInfo2: semantic.NewInt(section, cfg.Key("ids_info2")), + IdsInfo3: semantic.NewInt(section, cfg.Key("ids_info3")), + Nanobots: semantic.NewInt(section, cfg.Key("nanobot_limit")), + Batteries: semantic.NewInt(section, cfg.Key("shield_battery_limit")), + Mass: semantic.NewFloat(section, cfg.Key("mass"), semantic.Precision(2)), + LinearDrag: semantic.NewFloat(section, cfg.Key("linear_drag"), semantic.Precision(2)), + HoldSize: semantic.NewInt(section, cfg.Key("hold_size")), + NudgeForce: semantic.NewFloat(section, cfg.Key("nudge_force"), semantic.Precision(2)), + StrafeForce: semantic.NewFloat(section, cfg.Key("strafe_force"), semantic.Precision(2)), + ShieldLink: &ShieldLink{ + ShieldClass: semantic.NewString(section, cfg.Key("shield_link"), semantic.WithLowercaseS(), semantic.WithoutSpacesS()), + }, + HitPts: semantic.NewInt(section, cfg.Key("hit_pts")), + + SteeringTorque: semantic.NewVector(section, cfg.Key("steering_torque"), semantic.Precision(2)), + AngularDrag: semantic.NewVector(section, cfg.Key("angular_drag"), semantic.Precision(2)), + RotationIntertia: semantic.NewVector(section, cfg.Key("rotation_inertia"), semantic.Precision(2)), + + ArmorMult: semantic.NewFloat(section, cfg.Key("armor"), semantic.Precision(2), semantic.WithDefaultF(1.0)), + } + ship.Map(section) + ship.ShieldLink.Map(section) + + for index, param := range section.ParamMap[cfg.Key("hp_type")] { + hp_type := &HpType{ + Nickname: semantic.NewString(section, cfg.Key("hp_type"), + semantic.OptsS(semantic.Index(index)), + semantic.WithLowercaseS(), semantic.WithoutSpacesS()), + } + + for order_i, _ := range param.Values[1:] { + hp_type.AllowedEquipments = append(hp_type.AllowedEquipments, + semantic.NewString(section, cfg.Key("hp_type"), + semantic.OptsS(semantic.Index(index), semantic.Order(order_i+1)), + semantic.WithLowercaseS(), semantic.WithoutSpacesS()), + ) + } + + hp_type.Map(section) + ship.HpTypes = append(ship.HpTypes, hp_type) + } + + // ids_name, ids_name_exists := ship.IdsName.GetValue() + + frelconfig.Ships = append(frelconfig.Ships, ship) + frelconfig.ShipsMap[ship.Nickname.Get()] = ship + + } + } + + return frelconfig +} + +func (frelconfig *Config) Write() []*file.File { + var files []*file.File + for _, file := range frelconfig.Files { + inifile := file.Render() + inifile.Write(inifile.File) + files = append(files, inifile.File) + } + return files +} diff --git a/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/solar_mapped/loadouts_mapped/loadouts.go b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/solar_mapped/loadouts_mapped/loadouts.go new file mode 100644 index 0000000..9327b4f --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/solar_mapped/loadouts_mapped/loadouts.go @@ -0,0 +1,72 @@ +package loadouts_mapped + +import ( + "github.com/darklab8/fl-darkstat/configs/cfg" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/filefind/file" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/iniload" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/semantic" + "github.com/darklab8/go-utils/utils/utils_types" +) + +type Cargo struct { + semantic.Model + Nickname *semantic.String +} + +type Loadout struct { + semantic.Model + Nickname *semantic.String + Cargos []*Cargo +} + +type Config struct { + Files []*iniload.IniLoader + Loadouts []*Loadout + LoadoutsByNick map[string]*Loadout +} + +const ( + FILENAME utils_types.FilePath = "loadouts.ini" +) + +func Read(files []*iniload.IniLoader) *Config { + frelconfig := &Config{ + Files: files, + LoadoutsByNick: make(map[string]*Loadout), + } + for _, input_file := range files { + + for _, section := range input_file.SectionMap["[loadout]"] { + + loadout := &Loadout{ + Nickname: semantic.NewString(section, cfg.Key("nickname"), semantic.WithLowercaseS(), semantic.WithoutSpacesS()), + } + loadout.Map(section) + + cargo_key := cfg.Key("cargo") + for good_index, _ := range section.ParamMap[cargo_key] { + cargo := &Cargo{ + Nickname: semantic.NewString(section, cargo_key, semantic.WithLowercaseS(), semantic.OptsS(semantic.Index(good_index)), semantic.WithoutSpacesS()), + } + cargo.Map(section) + loadout.Cargos = append(loadout.Cargos, cargo) + } + + frelconfig.Loadouts = append(frelconfig.Loadouts, loadout) + frelconfig.LoadoutsByNick[loadout.Nickname.Get()] = loadout + + } + } + return frelconfig + +} + +func (frelconfig *Config) Write() []*file.File { + var files []*file.File + for _, file := range frelconfig.Files { + inifile := file.Render() + inifile.Write(inifile.File) + files = append(files, inifile.File) + } + return files +} diff --git a/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/solar_mapped/solararch_mapped/solararch.go b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/solar_mapped/solararch_mapped/solararch.go new file mode 100644 index 0000000..b26a048 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/solar_mapped/solararch_mapped/solararch.go @@ -0,0 +1,69 @@ +package solararch_mapped + +import ( + "github.com/darklab8/fl-darkstat/configs/cfg" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/filefind/file" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/iniload" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/semantic" + "github.com/darklab8/go-utils/utils/utils_types" +) + +type Solar struct { + semantic.Model + Nickname *semantic.String + DockingSpheres []*semantic.String +} + +func (solar *Solar) IsDockableByCaps() bool { + for _, docking_sphere := range solar.DockingSpheres { + if docking_sphere_name, dockable := docking_sphere.GetValue(); dockable { + if docking_sphere_name == "jump" || docking_sphere_name == "moor_large" { + return true + } + } + } + return false +} + +type Config struct { + *iniload.IniLoader + Solars []*Solar + SolarsByNick map[string]*Solar +} + +const ( + FILENAME utils_types.FilePath = "solararch.ini" +) + +func Read(input_file *iniload.IniLoader) *Config { + frelconfig := &Config{ + IniLoader: input_file, + SolarsByNick: make(map[string]*Solar), + } + + for _, section := range input_file.SectionMap["[solar]"] { + + solar := &Solar{ + Nickname: semantic.NewString(section, cfg.Key("nickname"), semantic.WithLowercaseS(), semantic.WithoutSpacesS()), + } + solar.Map(section) + + empathy_rate_key := cfg.Key("docking_sphere") + for good_index, _ := range section.ParamMap[empathy_rate_key] { + solar.DockingSpheres = append(solar.DockingSpheres, + semantic.NewString(section, cfg.Key("docking_sphere"), semantic.WithLowercaseS(), semantic.OptsS(semantic.Index(good_index)), semantic.WithoutSpacesS())) + } + + frelconfig.Solars = append(frelconfig.Solars, solar) + frelconfig.SolarsByNick[solar.Nickname.Get()] = solar + + } + return frelconfig + +} + +func (frelconfig *Config) Write() *file.File { + inifile := frelconfig.Render() + inifile.Write(inifile.File) + return inifile.File +} diff --git a/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/universe_mapped/systems_mapped/systems.go b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/universe_mapped/systems_mapped/systems.go new file mode 100644 index 0000000..9e114ce --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/universe_mapped/systems_mapped/systems.go @@ -0,0 +1,541 @@ +package systems_mapped + +import ( + "fmt" + "strings" + "sync" + + "github.com/darklab8/fl-darkstat/configs/cfg" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/universe_mapped" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/filefind" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/filefind/file" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/inireader" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/semantic" + "github.com/darklab8/go-utils/utils/timeit" + "github.com/darklab8/go-utils/utils/utils_types" +) + +const ( + KEY_OBJECT = "[object]" +) + +var ( + KEY_NICKNAME = cfg.Key("nickname") + KEY_BASE = cfg.Key("base") +) + +type MissionVignetteZone struct { + // [zone] + // nickname = Zone_BR07_destroy_vignette_02 + // pos = -39714, 0, -20328 + // shape = SPHERE + // size = 10000 + // mission_type = lawful, unlawful + // sort = 99.500000 + // vignette_type = field + + // vignettes + semantic.Model + Nickname *semantic.String + Size *semantic.Int + Shape *semantic.String + Pos *semantic.Vect + VignetteType *semantic.String + MissionType *semantic.String + + // it has mission_type = lawful, unlawful this. + // who is lawful and unlawful? :) + + // if has vignette_type = field + // Then it is Vignette +} + +type Patrol struct { + semantic.Model + FactionNickname *semantic.String + Chance *semantic.Float +} + +type MissionPatrolZone struct { + semantic.Model + Nickname *semantic.String + Size *semantic.Vect + Shape *semantic.String + Pos *semantic.Vect + + Factions []*Patrol + // [zone] + // nickname = Path_outcasts1_2 + // pos = -314, 0, -1553.2 + // rotate = 90, -75.2, 180 + // shape = CYLINDER + // size = 750, 50000 + // sort = 99 + // toughness = 14 + // density = 5 + // repop_time = 30 + // max_battle_size = 4 + // pop_type = attack_patrol + // relief_time = 20 + // path_label = BR07_outcasts1, 2 + // usage = patrol + // mission_eligible = True + // encounter = patrolp_assault, 14, 0.4 + // faction = fc_m_grp, 1.0 +} + +// TODO finish coding stuff with them +type PvEEncounter struct { + semantic.Model + Nickname *semantic.String + Pos *semantic.Vect + Size *semantic.Vect + Shape *semantic.String + + Density *semantic.Int // Max enemies spawned + MaxBattleSize *semantic.Int // Max enemies spawn in battle + RepopTime *semantic.Int // respawn speed + ReliefTime *semantic.Int + + Encounter []*Encounter +} + +// TODO finish coding stuff with them +type Encounter struct { + semantic.Model + Nickname *semantic.String + Difficulty *semantic.Float + ChanceToSpawn *semantic.Float +} + +type TradeLaneRing struct { + // [Object] + // nickname = BR07_Trade_Lane_Ring_3_1 + // ids_name = 260659 + // pos = -20293, 0, 21375 + // rotate = 0, 5, 0 + // archetype = Trade_Lane_Ring + // next_ring = BR07_Trade_Lane_Ring_3_2 + // ids_info = 66170 + // reputation = br_n_grp + // tradelane_space_name = 501168 + // difficulty_level = 1 + // loadout = trade_lane_ring_br_01 + // pilot = pilot_solar_easiest + semantic.Model + Nickname *semantic.String + Pos *semantic.Vect + NextRing *semantic.String + PrevRing *semantic.String + // has next_ring, then it is tradelane + // or if has Trade_Lane_Ring, then trade lane too. +} + +type Base struct { + semantic.Model + Nickname *semantic.String + Base *semantic.String // base.nickname in universe.ini + DockWith *semantic.String + Archetype *semantic.String + + IDsInfo *semantic.Int + IdsName *semantic.Int + RepNickname *semantic.String + Pos *semantic.Vect + System *System + Parent *semantic.String +} + +const ( + BaseArchetypeInvisible = "invisible_base" +) + +type Jumphole struct { + semantic.Model + Nickname *semantic.String + GotoHole *semantic.String + Archetype *semantic.String + Pos *semantic.Vect + IdsName *semantic.Int + + System *System +} + +type Object struct { + semantic.Model + Nickname *semantic.String +} + +type Wreck struct { + semantic.Model + Nickname *semantic.String + Loadout *semantic.String +} + +type Asteroids struct { + semantic.Model + File *semantic.Path + Zone *semantic.String + LootableZone *LootableZone +} + +type LootableZone struct { + semantic.Model + AsteroidLootCommodity *semantic.String + AsteroidLootMin *semantic.Int + AsteroidLootMax *semantic.Int + DynamicLootMin *semantic.Int + DynamicLootMax *semantic.Int + AsteroidLootDifficulty *semantic.Int + DynamicLootDifficulty *semantic.Int +} + +type Zone struct { + semantic.Model + Nickname *semantic.String + Pos *semantic.Vect + IDsInfo *semantic.Int + IdsName *semantic.Int +} + +type System struct { + semantic.ConfigModel + Nickname string + Bases []*Base + BasesByNick map[string]*Base + BasesByBases map[string]*Base + BasesByDockWith map[string]*Base + AllBasesByBases map[string][]*Base + AllBasesByDockWith map[string][]*Base + Jumpholes []*Jumphole + Tradelanes []*TradeLaneRing + TradelaneByNick map[string]*TradeLaneRing + + MissionZoneVignettes []*MissionVignetteZone + + MissionsSpawnZone []*MissionPatrolZone + MissionsSpawnZonesByFaction map[string][]*MissionPatrolZone + + Asteroids []*Asteroids + ZonesByNick map[string]*Zone + Objects []*Object + Wrecks []*Wreck +} + +type Config struct { + SystemsMap map[string]*System + Systems []*System + + // it can contain more than one base meeting condition. + BasesByBases map[string]*Base + BasesByDockWith map[string]*Base + BasesByNick map[string]*Base + JumpholesByNick map[string]*Jumphole +} + +type FileRead struct { + system_key string + file *file.File + ini *inireader.INIFile +} + +func Read(universe_config *universe_mapped.Config, filesystem *filefind.Filesystem) *Config { + frelconfig := &Config{ + BasesByBases: make(map[string]*Base), + BasesByDockWith: make(map[string]*Base), + + BasesByNick: make(map[string]*Base), + JumpholesByNick: make(map[string]*Jumphole), + } + var wg sync.WaitGroup + + var system_files map[string]*file.File = make(map[string]*file.File) + + timeit.NewTimerF(func() { + for _, base := range universe_config.Bases { + base_system := base.System.Get() + universe_system := universe_config.SystemMap[universe_mapped.SystemNickname(base_system)] + filename := universe_system.File.FileName() + path := filesystem.GetFile(filename) + system_files[base.System.Get()] = file.NewFile(path.GetFilepath()) + } + }, timeit.WithMsg("systems prepared files")) + + var system_iniconfigs map[string]*inireader.INIFile = make(map[string]*inireader.INIFile) + + func() { + timeit.NewTimerF(func() { + // Read system files with parallelism ^_^ + iniconfigs_channel := make(chan *FileRead) + read_file := func(data *FileRead) { + data.ini = inireader.Read(data.file) + iniconfigs_channel <- data + } + for system_key, file := range system_files { + go read_file(&FileRead{ + system_key: system_key, + file: file, + }) + } + for range system_files { + result := <-iniconfigs_channel + system_iniconfigs[result.system_key] = result.ini + } + }, timeit.WithMsg("Read system files with parallelism ^_^")) + }() + + timeit.NewTimerF(func() { + frelconfig.SystemsMap = make(map[string]*System) + frelconfig.Systems = make([]*System, 0) + for system_key, sysiniconf := range system_iniconfigs { + system_to_add := &System{ + MissionsSpawnZonesByFaction: make(map[string][]*MissionPatrolZone), + TradelaneByNick: make(map[string]*TradeLaneRing), + ZonesByNick: make(map[string]*Zone), + AllBasesByBases: make(map[string][]*Base), + AllBasesByDockWith: make(map[string][]*Base), + + BasesByNick: make(map[string]*Base), + BasesByBases: make(map[string]*Base), + BasesByDockWith: make(map[string]*Base), + } + system_to_add.Init(sysiniconf.Sections, sysiniconf.Comments, sysiniconf.File.GetFilepath()) + + system_to_add.Nickname = system_key + + system_to_add.Bases = make([]*Base, 0) + frelconfig.SystemsMap[system_key] = system_to_add + frelconfig.Systems = append(frelconfig.Systems, system_to_add) + + if asteroids, ok := sysiniconf.SectionMap["[asteroids]"]; ok { + for _, obj := range asteroids { + asteroids_to_add := &Asteroids{ + File: semantic.NewPath(obj, cfg.Key("file")), + Zone: semantic.NewString(obj, cfg.Key("zone"), semantic.WithLowercaseS(), semantic.WithoutSpacesS()), + } + asteroids_to_add.Map(obj) + + system_to_add.Asteroids = append(system_to_add.Asteroids, asteroids_to_add) + + wg.Add(1) + go func(ast *Asteroids) { + filename := ast.File.FileName() + + file_to_read := filesystem.GetFile(utils_types.FilePath(strings.ToLower(filename.ToString()))) + if file_to_read == nil { + fmt.Println("not able to find mining file for asteroids zone", filename) + wg.Done() + return + } + config := inireader.Read(file_to_read) + + if lootable_zones, ok := config.SectionMap["[lootablezone]"]; ok { + obj := lootable_zones[0] + lootable_zone := &LootableZone{ + AsteroidLootCommodity: semantic.NewString(obj, cfg.Key("asteroid_loot_commodity"), semantic.WithLowercaseS(), semantic.WithoutSpacesS()), + + AsteroidLootMin: semantic.NewInt(obj, cfg.Key("asteroid_loot_count"), semantic.Optional(), semantic.Order(0)), + AsteroidLootMax: semantic.NewInt(obj, cfg.Key("asteroid_loot_count"), semantic.Optional(), semantic.Order(1)), + DynamicLootMin: semantic.NewInt(obj, cfg.Key("dynamic_loot_count"), semantic.Optional(), semantic.Order(0)), + DynamicLootMax: semantic.NewInt(obj, cfg.Key("dynamic_loot_count"), semantic.Optional(), semantic.Order(1)), + AsteroidLootDifficulty: semantic.NewInt(obj, cfg.Key("asteroid_loot_difficulty"), semantic.Optional()), + DynamicLootDifficulty: semantic.NewInt(obj, cfg.Key("dynamic_loot_difficulty"), semantic.Optional()), + } + lootable_zone.Map(obj) + ast.LootableZone = lootable_zone + } + wg.Done() + + }(asteroids_to_add) + } + } + if objects, ok := sysiniconf.SectionMap[KEY_OBJECT]; ok { + for _, obj := range objects { + + object_to_add := &Object{ + Nickname: semantic.NewString(obj, cfg.Key("nickname"), semantic.WithLowercaseS(), semantic.WithoutSpacesS()), + } + object_to_add.Map(obj) + system_to_add.Objects = append(system_to_add.Objects, object_to_add) + + // check if it is base object + _, has_base := obj.ParamMap[KEY_BASE] + _, has_dock_with := obj.ParamMap[cfg.Key("dock_with")] + _ = has_dock_with + if has_base || has_dock_with { // || has_dock_with + base_to_add := &Base{ + Archetype: semantic.NewString(obj, cfg.Key("archetype"), semantic.WithLowercaseS(), semantic.WithoutSpacesS()), + Parent: semantic.NewString(obj, cfg.Key("parent"), semantic.WithLowercaseS(), semantic.WithoutSpacesS()), + Nickname: semantic.NewString(obj, KEY_NICKNAME, semantic.WithLowercaseS(), semantic.WithoutSpacesS()), + Base: semantic.NewString(obj, KEY_BASE, semantic.WithLowercaseS(), semantic.WithoutSpacesS()), + DockWith: semantic.NewString(obj, cfg.Key("dock_with"), semantic.WithLowercaseS(), semantic.WithoutSpacesS()), + RepNickname: semantic.NewString(obj, cfg.Key("reputation"), semantic.OptsS(semantic.Optional()), semantic.WithLowercaseS(), semantic.WithoutSpacesS()), + IDsInfo: semantic.NewInt(obj, cfg.Key("ids_info"), semantic.Optional()), + IdsName: semantic.NewInt(obj, cfg.Key("ids_name"), semantic.Optional()), + Pos: semantic.NewVector(obj, cfg.Key("pos"), semantic.Precision(0)), + System: system_to_add, + } + base_to_add.Map(obj) + + system_to_add.BasesByNick[base_to_add.Nickname.Get()] = base_to_add + + if base, ok := base_to_add.Base.GetValue(); ok { + if _, ok := system_to_add.BasesByBases[base]; !ok { + system_to_add.BasesByBases[base] = base_to_add + } + system_to_add.AllBasesByBases[base] = append(system_to_add.AllBasesByBases[base], base_to_add) + + if _, ok := frelconfig.BasesByBases[base]; !ok { + frelconfig.BasesByBases[base] = base_to_add + } + + } + + if dock_with_base, ok := base_to_add.DockWith.GetValue(); ok { + if _, ok := system_to_add.AllBasesByDockWith[dock_with_base]; !ok { + system_to_add.AllBasesByDockWith[dock_with_base] = append(system_to_add.AllBasesByDockWith[dock_with_base], base_to_add) + } + system_to_add.AllBasesByDockWith[dock_with_base] = append(system_to_add.AllBasesByDockWith[dock_with_base], base_to_add) + + if _, ok := frelconfig.BasesByDockWith[dock_with_base]; !ok { + frelconfig.BasesByDockWith[dock_with_base] = base_to_add + } + + } + + system_to_add.Bases = append(system_to_add.Bases, base_to_add) + + if base_nickname, ok := base_to_add.Nickname.GetValue(); ok { + frelconfig.BasesByNick[base_nickname] = base_to_add + } + + } + + if _, ok := obj.ParamMap[cfg.Key("goto")]; ok { + jumphole := &Jumphole{ + Archetype: semantic.NewString(obj, cfg.Key("archetype"), semantic.WithLowercaseS(), semantic.WithoutSpacesS()), + Nickname: semantic.NewString(obj, cfg.Key("nickname"), semantic.WithLowercaseS(), semantic.WithoutSpacesS()), + GotoHole: semantic.NewString(obj, cfg.Key("goto"), semantic.WithLowercaseS(), semantic.WithoutSpacesS(), semantic.OptsS(semantic.Order(1))), + Pos: semantic.NewVector(obj, cfg.Key("pos"), semantic.Precision(0)), + IdsName: semantic.NewInt(obj, cfg.Key("ids_name"), semantic.Optional()), + System: system_to_add, + } + + system_to_add.Jumpholes = append(system_to_add.Jumpholes, jumphole) + frelconfig.JumpholesByNick[jumphole.Nickname.Get()] = jumphole + } + + if _, ok := obj.ParamMap[cfg.Key("loadout")]; ok { + wreck := &Wreck{ + Nickname: semantic.NewString(obj, cfg.Key("nickname"), semantic.WithLowercaseS(), semantic.WithoutSpacesS()), + Loadout: semantic.NewString(obj, cfg.Key("loadout"), semantic.WithLowercaseS(), semantic.WithoutSpacesS()), + } + + system_to_add.Wrecks = append(system_to_add.Wrecks, wreck) + } + + _, is_trade_lane1 := obj.ParamMap[cfg.Key("next_ring")] + _, is_trade_lane2 := obj.ParamMap[cfg.Key("prev_ring")] + if is_trade_lane1 || is_trade_lane2 { + tradelane := &TradeLaneRing{ + Nickname: semantic.NewString(obj, cfg.Key("nickname"), semantic.WithLowercaseS(), semantic.WithoutSpacesS()), + Pos: semantic.NewVector(obj, cfg.Key("pos"), semantic.Precision(0)), + NextRing: semantic.NewString(obj, cfg.Key("next_ring"), semantic.WithLowercaseS(), semantic.WithoutSpacesS()), + PrevRing: semantic.NewString(obj, cfg.Key("prev_ring"), semantic.WithLowercaseS(), semantic.WithoutSpacesS()), + } + + system_to_add.Tradelanes = append(system_to_add.Tradelanes, tradelane) + system_to_add.TradelaneByNick[tradelane.Nickname.Get()] = tradelane + } + + } + } + + if zones, ok := sysiniconf.SectionMap["[zone]"]; ok { + for _, zone_info := range zones { + + zone_to_add := &Zone{ + Nickname: semantic.NewString(zone_info, cfg.Key("nickname"), semantic.WithLowercaseS(), semantic.WithoutSpacesS()), + Pos: semantic.NewVector(zone_info, cfg.Key("pos"), semantic.Precision(0)), + IdsName: semantic.NewInt(zone_info, cfg.Key("ids_name"), semantic.Optional()), + IDsInfo: semantic.NewInt(zone_info, cfg.Key("ids_info"), semantic.Optional()), + } + system_to_add.ZonesByNick[zone_to_add.Nickname.Get()] = zone_to_add + + if vignette_type, ok := zone_info.ParamMap[cfg.Key("vignette_type")]; ok && len(vignette_type) > 0 { + vignette := &MissionVignetteZone{ + Nickname: semantic.NewString(zone_info, cfg.Key("nickname"), semantic.WithLowercaseS(), semantic.WithoutSpacesS()), + Size: semantic.NewInt(zone_info, cfg.Key("size"), semantic.Optional()), + Shape: semantic.NewString(zone_info, cfg.Key("shape"), semantic.WithLowercaseS(), semantic.WithoutSpacesS()), + Pos: semantic.NewVector(zone_info, cfg.Key("pos"), semantic.Precision(2)), + VignetteType: semantic.NewString(zone_info, cfg.Key("vignette_type"), semantic.WithLowercaseS(), semantic.WithoutSpacesS()), + MissionType: semantic.NewString(zone_info, cfg.Key("mission_type"), semantic.WithLowercaseS(), semantic.WithoutSpacesS()), + } + vignette.Map(zone_info) + system_to_add.MissionZoneVignettes = append(system_to_add.MissionZoneVignettes, vignette) + } + + if identifier, ok := zone_info.ParamMap[cfg.Key("faction")]; ok && len(identifier) > 0 { + spawn_area := &MissionPatrolZone{ + Nickname: semantic.NewString(zone_info, cfg.Key("nickname"), semantic.WithLowercaseS(), semantic.WithoutSpacesS()), + Size: semantic.NewVector(zone_info, cfg.Key("size"), semantic.Precision(2)), + Shape: semantic.NewString(zone_info, cfg.Key("shape"), semantic.WithLowercaseS(), semantic.WithoutSpacesS()), + Pos: semantic.NewVector(zone_info, cfg.Key("pos"), semantic.Precision(2)), + } + spawn_area.Map(zone_info) + + if factions, ok := zone_info.ParamMap[cfg.Key("faction")]; ok { + for index := range factions { + faction := &Patrol{ + FactionNickname: semantic.NewString(zone_info, cfg.Key("faction"), + semantic.WithLowercaseS(), semantic.WithoutSpacesS(), semantic.OptsS(semantic.Index(index), semantic.Order(0))), + Chance: semantic.NewFloat(zone_info, cfg.Key("faction"), semantic.Precision(2), semantic.OptsF(semantic.Index(index), semantic.Order(1))), + } + faction.Map(zone_info) + spawn_area.Factions = append(spawn_area.Factions, faction) + } + } + + system_to_add.MissionsSpawnZone = append(system_to_add.MissionsSpawnZone, spawn_area) + + for _, faction := range spawn_area.Factions { + faction_nickname := faction.FactionNickname.Get() + system_to_add.MissionsSpawnZonesByFaction[faction_nickname] = append(system_to_add.MissionsSpawnZonesByFaction[faction_nickname], spawn_area) + } + } + } + } + } + }, timeit.WithMsg("Map universe itself")) + + wg.Wait() + + // Making sure we selected Parent Bases to return + for _, base := range frelconfig.BasesByBases { + if parent_nickname, ok := base.Parent.GetValue(); ok { + if main_base, ok := frelconfig.BasesByNick[parent_nickname]; ok { + frelconfig.BasesByBases[main_base.Base.Get()] = main_base + } + } + } + for _, system := range frelconfig.Systems { + for _, base := range system.BasesByBases { + if parent_nickname, ok := base.Parent.GetValue(); ok { + if main_base, ok := system.BasesByNick[parent_nickname]; ok { + system.BasesByBases[main_base.Base.Get()] = main_base + } + } + } + } + + return frelconfig +} + +func (frelconfig *Config) Write() []*file.File { + var files []*file.File = make([]*file.File, 0) + for _, system := range frelconfig.Systems { + inifile := system.Render() + files = append(files, inifile.Write(inifile.File)) + } + return files +} diff --git a/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/universe_mapped/universe.go b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/universe_mapped/universe.go new file mode 100644 index 0000000..cd997b0 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/universe_mapped/universe.go @@ -0,0 +1,280 @@ +/* +parse universe.ini +*/ +package universe_mapped + +import ( + "strings" + + "github.com/darklab8/fl-darkstat/configs/cfg" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/filefind" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/filefind/file" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/iniload" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/inireader" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/semantic" + "github.com/darklab8/go-utils/utils/timeit" + "github.com/darklab8/go-utils/utils/utils_types" +) + +// Feel free to map it xD +// terrain_tiny = nonmineable_asteroid90 +// terrain_sml = nonmineable_asteroid60 +// terrain_mdm = nonmineable_asteroid90 +// terrain_lrg = nonmineable_asteroid60 +// terrain_dyna_01 = mineable1_asteroid10 +// terrain_dyna_02 = mineable1_asteroid10 + +var KEY_BASE_TERRAINS = [...]string{"terrain_tiny", "terrain_sml", "terrain_mdm", "terrain_lrg", "terrain_dyna_01", "terrain_dyna_02"} + +var ( + KEY_NICKNAME = cfg.Key("nickname") + KEY_STRIDNAME = cfg.Key("strid_name") + KEY_SYSTEM = cfg.Key("system") + KEY_FILE = cfg.Key("file") + + KEY_BASE_BGCS = cfg.Key("BGCS_base_run_by") + + KEY_SYSTEM_MSG_ID_PREFIX = cfg.Key("msg_id_prefix") + KEY_SYSTEM_VISIT = cfg.Key("visit") + KEY_SYSTEM_IDS_INFO = cfg.Key("ids_info") + KEY_SYSTEM_NAVMAPSCALE = cfg.Key("NavMapScale") + KEY_SYSTEM_POS = cfg.Key("pos") + + KEY_TIME_SECONDS = cfg.Key("seconds_per_day") +) + +const ( + FILENAME = "universe.ini" + KEY_BASE_TAG = "[base]" + KEY_TIME_TAG = "[time]" + KEY_SYSTEM_TAG = "[system]" +) + +type Base struct { + semantic.Model + + Nickname *semantic.String + System *semantic.String + StridName *semantic.Int + File *semantic.Path + BGCS_base_run_by *semantic.String + // Terrains *semantic.StringStringMap + + ConfigBase *ConfigBase + + TraderExists bool +} + +type BaseNickname string + +type SystemNickname string + +type System struct { + semantic.Model + Nickname *semantic.String + // Pos *semantic.Pos + Msg_id_prefix *semantic.String + Visit *semantic.Int + StridName *semantic.Int + Ids_info *semantic.Int + File *semantic.Path + NavMapScale *semantic.Float + + PosX *semantic.Float + PosY *semantic.Float +} + +type Config struct { + File *iniload.IniLoader + Bases []*Base + BasesMap map[BaseNickname]*Base + + Systems []*System + SystemMap map[SystemNickname]*System + + TimeSeconds *semantic.Int +} + +type FileRead struct { + base_nickname string + file *file.File + ini *inireader.INIFile +} + +type RoomInfoRead struct { + Base *Base + Room *Room + file *file.File + ini *inireader.INIFile +} + +type Room struct { + semantic.Model + Nickname *semantic.String + File *semantic.Path + + RoomInfo +} + +type RoomInfo struct { + HotSpots []*HotSpot +} +type HotSpot struct { + semantic.Model + Name *semantic.String + RoomSwitch *semantic.String +} + +type ConfigBase struct { + File *inireader.INIFile + Rooms []*Room + RoomMapByRoomNickname map[string]*Room +} + +func Read(ini *iniload.IniLoader, filesystem *filefind.Filesystem) *Config { + frelconfig := &Config{File: ini} + + frelconfig.TimeSeconds = semantic.NewInt(ini.SectionMap[KEY_TIME_TAG][0], KEY_TIME_SECONDS) + frelconfig.BasesMap = make(map[BaseNickname]*Base) + frelconfig.Bases = make([]*Base, 0) + frelconfig.SystemMap = make(map[SystemNickname]*System) + frelconfig.Systems = make([]*System, 0) + + if bases, ok := ini.SectionMap[KEY_BASE_TAG]; ok { + for _, base := range bases { + base_to_add := &Base{} + base_to_add.Map(base) + base_to_add.Nickname = semantic.NewString(base, KEY_NICKNAME, semantic.WithLowercaseS(), semantic.WithoutSpacesS()) + base_to_add.StridName = semantic.NewInt(base, KEY_STRIDNAME) + base_to_add.BGCS_base_run_by = semantic.NewString(base, KEY_BASE_BGCS, semantic.OptsS(semantic.Optional())) + base_to_add.System = semantic.NewString(base, KEY_SYSTEM, semantic.WithLowercaseS(), semantic.WithoutSpacesS()) + base_to_add.File = semantic.NewPath(base, KEY_FILE, semantic.WithLowercaseP()) + + frelconfig.Bases = append(frelconfig.Bases, base_to_add) + frelconfig.BasesMap[BaseNickname(base_to_add.Nickname.Get())] = base_to_add + } + } + + // Reading Base Files + var base_files map[string]*file.File = make(map[string]*file.File) + timeit.NewTimerF(func() { + for _, base := range frelconfig.Bases { + filename := base.File.FileName() + path := filesystem.GetFile(filename) + base_files[base.Nickname.Get()] = file.NewFile(path.GetFilepath()) + } + }, timeit.WithMsg("systems prepared files")) + var base_fileconfigs map[string]*inireader.INIFile = make(map[string]*inireader.INIFile) + func() { + timeit.NewTimerF(func() { + // Read system files with parallelism ^_^ + iniconfigs_channel := make(chan *FileRead) + read_file := func(data *FileRead) { + data.ini = inireader.Read(data.file) + iniconfigs_channel <- data + } + for base_nickname, file := range base_files { + go read_file(&FileRead{ + base_nickname: base_nickname, + file: file, + }) + } + for range base_files { + result := <-iniconfigs_channel + base_fileconfigs[result.base_nickname] = result.ini + } + }, timeit.WithMsg("Read system files with parallelism ^_^")) + }() + // Enhancing bases with Base File info about Rooms inside + for _, base := range frelconfig.Bases { + if base_file, ok := base_fileconfigs[base.Nickname.Get()]; ok { + base.ConfigBase = &ConfigBase{ + File: base_file, + RoomMapByRoomNickname: make(map[string]*Room), + } + + if rooms, ok := base_file.SectionMap["[room]"]; ok { + for _, room_info := range rooms { + room := &Room{ + Nickname: semantic.NewString(room_info, cfg.Key("nickname"), semantic.WithLowercaseS(), semantic.WithoutSpacesS()), + File: semantic.NewPath(room_info, cfg.Key("file")), + } + room.Map(room_info) + base.ConfigBase.Rooms = append(base.ConfigBase.Rooms, room) + base.ConfigBase.RoomMapByRoomNickname[room.Nickname.Get()] = room + } + } + } + } + + //Read Room Infos with parallelism + iniconfigs_channel := make(chan *RoomInfoRead) + readd_room_info := func(data *RoomInfoRead) { + data.ini = inireader.Read(data.file) + iniconfigs_channel <- data + } + for _, base := range frelconfig.Bases { + for _, room := range base.ConfigBase.Rooms { + filename := room.File.FileName() + path := filesystem.GetFile(utils_types.FilePath(strings.ToLower(string(filename)))) + go readd_room_info(&RoomInfoRead{ + Room: room, + file: file.NewFile(path.GetFilepath()), + Base: base, + }) + } + + } + // Awaiting room info reading + for _, base := range frelconfig.Bases { + for _, _ = range base.ConfigBase.Rooms { + room_info := <-iniconfigs_channel + if sections, ok := room_info.ini.SectionMap["[hotspot]"]; ok { + for _, hot_spot_section := range sections { + hot_spot := &HotSpot{ + Name: semantic.NewString(hot_spot_section, cfg.Key("name"), semantic.WithLowercaseS(), semantic.WithoutSpacesS()), + RoomSwitch: semantic.NewString(hot_spot_section, cfg.Key("room_switch")), + } + hot_spot.Map(hot_spot_section) + room_info.Room.HotSpots = append(room_info.Room.HotSpots, hot_spot) + + if room_switch, ok := hot_spot.RoomSwitch.GetValue(); ok { + if strings.ToLower(room_switch) == "trader" { + room_info.Base.TraderExists = true + // fmt.Println("found, trader exists for base_nickname=", base.Nickname.Get()) + } + } + } + } + } + } + + if systems, ok := ini.SectionMap[KEY_SYSTEM_TAG]; ok { + for _, system := range systems { + system_to_add := System{ + NavMapScale: semantic.NewFloat(system, cfg.Key("NavMapScale"), semantic.Precision(2)), + PosX: semantic.NewFloat(system, cfg.Key("pos"), semantic.Precision(2), semantic.OptsF(semantic.Order(0))), + PosY: semantic.NewFloat(system, cfg.Key("pos"), semantic.Precision(2), semantic.OptsF(semantic.Order(1))), + } + system_to_add.Map(system) + + system_to_add.Visit = semantic.NewInt(system, KEY_SYSTEM_VISIT, semantic.Optional()) + system_to_add.StridName = semantic.NewInt(system, KEY_STRIDNAME, semantic.Optional()) + system_to_add.Ids_info = semantic.NewInt(system, KEY_SYSTEM_IDS_INFO, semantic.Optional()) + system_to_add.Nickname = semantic.NewString(system, KEY_NICKNAME, semantic.WithLowercaseS(), semantic.WithoutSpacesS()) + system_to_add.File = semantic.NewPath(system, KEY_FILE, semantic.WithLowercaseP()) + system_to_add.Msg_id_prefix = semantic.NewString(system, KEY_SYSTEM_MSG_ID_PREFIX, semantic.OptsS(semantic.Optional())) + + frelconfig.Systems = append(frelconfig.Systems, &system_to_add) + frelconfig.SystemMap[SystemNickname(system_to_add.Nickname.Get())] = &system_to_add + } + } + + return frelconfig +} + +func (frelconfig *Config) Write() *file.File { + inifile := frelconfig.File.Render() + inifile.Write(inifile.File) + return inifile.File +} diff --git a/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/exe_mapped/dll.go b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/exe_mapped/dll.go new file mode 100644 index 0000000..d53ea32 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/exe_mapped/dll.go @@ -0,0 +1,302 @@ +package exe_mapped + +/* +This file is an adaptation of LibreLancer's DLL processing code. +The original C# code is licensed under the MIT License and available here: +https://github.com/Librelancer/Librelancer/blob/main/src/LibreLancer.Data/Dll/ResourceDll.cs +*/ + +import ( + "bytes" + "encoding/binary" + "errors" + "math" + "os" + "strings" + "unicode/utf16" + + "github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/infocard_mapped/infocard" + "github.com/darklab8/fl-darkstat/configs/configs_settings" + "github.com/darklab8/go-typelog/typelog" + + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/filefind" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/filefind/file" + "github.com/darklab8/fl-darkstat/configs/configs_settings/logus" + "github.com/darklab8/go-utils/utils/utils_types" +) + +type IMAGE_RESOURCE_DIRECTORY struct { + Characteristics uint32 + TimeDateStamp uint32 + MajorVersion uint16 + MinorVersion uint16 + NumberOfNamedEntries uint16 + NumberOfIdEntries uint16 +} + +type IMAGE_RESOURCE_DIRECTORY_ENTRY struct { + Name uint32 + OffsetToData uint32 +} + +type IMAGE_RESOURCE_DATA_ENTRY struct { + OffsetToData uint32 + Size uint32 + CodePage uint32 + Reserved uint32 +} + +type ResourceTable struct { + Type uint32 + Resources []Resource +} + +type Resource struct { + Name uint32 + Locales []ResourceData +} + +type ResourceData struct { + Locale uint32 + Data []byte +} + +const ( + RT_RCDATA = 23 + RT_STRING = 6 + IMAGE_RESOURCE_NAME_IS_STRING = 0x80000000 + IMAGE_RESOURCE_DATA_IS_DIRECTORY = 0x80000000 +) + +type ResourceDll struct { + Strings map[int]string + Infocards map[int]string + Dialogs []BinaryResource + Menus []BinaryResource + VersionInfo *VersionInfoResource + SavePath string +} + +type BinaryResource struct { + Name uint32 + Data []byte +} + +type VersionInfoResource struct { + Data []byte +} + +func ParseDLL(fileData []byte, out *infocard.Config, globalOffset int) { + rsrcOffset, rsrc, err := ReadPE(fileData) + if err != nil { + panic(err) + } + + directory, err := Struct[IMAGE_RESOURCE_DIRECTORY](rsrc, 0) + if err != nil { + panic(err) + } + var resources []ResourceTable + for i := 0; i < int(directory.NumberOfNamedEntries+directory.NumberOfIdEntries); i++ { + off := 16 + (i * 8) + entry, err := Struct[IMAGE_RESOURCE_DIRECTORY_ENTRY](rsrc, off) + if err != nil { + panic(err) + } + if (IMAGE_RESOURCE_NAME_IS_STRING & entry.Name) == IMAGE_RESOURCE_NAME_IS_STRING { + continue + } + table, err := ReadResourceTable(rsrcOffset, DirOffset(entry.OffsetToData), rsrc, entry.Name) + if err != nil { + panic(err) + } + resources = append(resources, table) + } + + for _, table := range resources { + switch table.Type { + case RT_RCDATA: + for _, res := range table.Resources { + idsId := globalOffset + int(res.Name) + idx := 0 + count := len(res.Locales[0].Data) + if count > 2 { + if count%2 == 1 && res.Locales[0].Data[count-1] == 0 { + count-- + } + if res.Locales[0].Data[0] == 0xFF && res.Locales[0].Data[1] == 0xFE { + idx += 2 + } + } + str, err := decodeUnicode(res.Locales[0].Data[idx:count]) + if err != nil { + logus.Log.Warn("Infocard corrupt, skipping.", typelog.Any("id", idsId), typelog.Any("error", err)) + continue + } + out.Infocards[idsId] = infocard.NewInfocard(str) + } + case RT_STRING: + for _, res := range table.Resources { + blockId := globalOffset + int(res.Name-1)*16 + seg := res.Locales[0].Data + reader := bytes.NewReader(seg) + for j := 0; j < 16; j++ { + var length uint16 + binary.Read(reader, binary.LittleEndian, &length) + length *= 2 + if length != 0 { + bytes := make([]byte, length) + reader.Read(bytes) + idsId := blockId + j + str, err := decodeUnicode(bytes) + if err != nil { + logus.Log.Warn("Infostring corrupt, skipping.", typelog.Any("id", idsId), typelog.Any("error", err)) + continue + } + out.Infonames[idsId] = infocard.Infoname(str) + } + } + } + } + } +} + +func DirOffset(a uint32) int { + return int(a & 0x7FFFFFFF) +} + +func ReadResourceTable(rsrcOffset uint32, offset int, rsrc []byte, rtype uint32) (ResourceTable, error) { + directory, err := Struct[IMAGE_RESOURCE_DIRECTORY](rsrc, offset) + if err != nil { + return ResourceTable{}, err + } + table := ResourceTable{Type: rtype} + for i := 0; i < int(directory.NumberOfNamedEntries+directory.NumberOfIdEntries); i++ { + off := offset + 16 + (i * 8) + entry, err := Struct[IMAGE_RESOURCE_DIRECTORY_ENTRY](rsrc, off) + if err != nil { + return ResourceTable{}, err + } + res := Resource{Name: entry.Name} + if (IMAGE_RESOURCE_DATA_IS_DIRECTORY & entry.OffsetToData) == IMAGE_RESOURCE_DATA_IS_DIRECTORY { + langDirectory, err := Struct[IMAGE_RESOURCE_DIRECTORY](rsrc, DirOffset(entry.OffsetToData)) + if err != nil { + return ResourceTable{}, err + } + for j := 0; j < int(langDirectory.NumberOfIdEntries+langDirectory.NumberOfNamedEntries); j++ { + langOff := DirOffset(entry.OffsetToData) + 16 + (j * 8) + langEntry, err := Struct[IMAGE_RESOURCE_DIRECTORY_ENTRY](rsrc, langOff) + if err != nil { + return ResourceTable{}, err + } + if (IMAGE_RESOURCE_DATA_IS_DIRECTORY & langEntry.OffsetToData) == IMAGE_RESOURCE_DATA_IS_DIRECTORY { + return ResourceTable{}, errors.New("malformed .rsrc section") + } + dataEntry, err := Struct[IMAGE_RESOURCE_DATA_ENTRY](rsrc, int(langEntry.OffsetToData)) + if err != nil { + return ResourceTable{}, err + } + dat := rsrc[(dataEntry.OffsetToData - rsrcOffset):(dataEntry.OffsetToData - rsrcOffset + dataEntry.Size)] + res.Locales = append(res.Locales, ResourceData{Locale: langEntry.Name, Data: dat}) + } + } else { + return ResourceTable{}, errors.New("malformed .rsrc section") + } + table.Resources = append(table.Resources, res) + } + return table, nil +} + +func Struct[T any](rawData []byte, offset int) (T, error) { + var data T + buf := bytes.NewReader(rawData[offset:]) + err := binary.Read(buf, binary.LittleEndian, &data) + return data, err +} + +func ReadPE(fullImage []byte) (uint32, []byte, error) { + // Read the DOS header + peOffset := binary.LittleEndian.Uint32(fullImage[60:64]) + peHeader := fullImage[peOffset:] + + // Read the PE header + numberOfSections := binary.LittleEndian.Uint16(peHeader[6:8]) + sizeOfOptionalHeader := binary.LittleEndian.Uint16(peHeader[20:22]) + sectionTable := peHeader[24+sizeOfOptionalHeader:] + + // Find the resource section + var rawStart, offset uint32 + for i := 0; i < int(numberOfSections); i++ { + section := sectionTable[i*40 : (i+1)*40] + sectionName := string(bytes.Trim(section[:8], "\x00")) + if sectionName == ".rsrc" { + offset = binary.LittleEndian.Uint32(section[12:16]) + // rsrcSize = binary.LittleEndian.Uint32(section[16:20]) + rawStart = binary.LittleEndian.Uint32(section[20:24]) + break + } + } + + if rawStart == 0 { + logus.Log.Error("Resource section not found") + return 0, nil, errors.New("resource section not found") + } + + array := fullImage[rawStart:] + return offset, array, nil +} + +func decodeUnicode(b []byte) (string, error) { + if len(b)%2 != 0 { + return "", errors.New("invalid byte array length for Unicode string") + } + u16 := make([]uint16, len(b)/2) + for i := 0; i < len(u16); i++ { + u16[i] = binary.LittleEndian.Uint16(b[i*2:]) + } + runes := utf16.Decode(u16) + return string(runes), nil +} + +func ParseDLLs(dll_fnames []*file.File) *infocard.Config { + out := infocard.NewConfig() + + for idx, name := range dll_fnames { + data, err := os.ReadFile(name.GetFilepath().ToString()) + + if logus.Log.CheckError(err, "unable to read dll") { + continue + } + + // if you inject "resources.dll" as 0 element of the list to process + // despite it being not present in freelancer.ini and original Alex parsing script + // then we go with global_offset from (idx) instead of (idx+1) as Alex had. + global_offset := int(math.Pow(2, 16)) * (idx) + + func() { + defer func() { + if r := recover(); r != nil { + logus.Log.Error("unable to read dll. Recovered by skipping dll.", typelog.String("filepath", name.GetFilepath().ToString()), typelog.Any("recover", r)) + if configs_settings.Env.Strict { + panic(r) + } + } + }() + ParseDLL(data, out, global_offset) + }() + + } + + return out +} + +func GetAllInfocards(filesystem *filefind.Filesystem, dll_names []string) *infocard.Config { + + var files []*file.File + for _, filename := range dll_names { + dll_file := filesystem.GetFile(utils_types.FilePath(strings.ToLower(filename))) + files = append(files, dll_file) + } + + return ParseDLLs(files) +} diff --git a/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/exe_mapped/fixtures.go b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/exe_mapped/fixtures.go new file mode 100644 index 0000000..d971b6b --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/exe_mapped/fixtures.go @@ -0,0 +1,12 @@ +package exe_mapped + +import ( + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/iniload" + "github.com/darklab8/fl-darkstat/configs/tests" +) + +func FixtureFLINIConfig() *Config { + fileref := tests.FixtureFileFind().GetFile(FILENAME_FL_INI) + config := Read(iniload.NewLoader(fileref).Scan()) + return config +} diff --git a/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/exe_mapped/freelancer_ini.go b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/exe_mapped/freelancer_ini.go new file mode 100644 index 0000000..3312506 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/exe_mapped/freelancer_ini.go @@ -0,0 +1,86 @@ +package exe_mapped + +import ( + "github.com/darklab8/fl-darkstat/configs/cfg" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/filefind/file" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/iniload" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/semantic" + "github.com/darklab8/go-utils/utils" +) + +var KEY_BASE_TERRAINS = [...]string{"terrain_tiny", "terrain_sml", "terrain_mdm", "terrain_lrg", "terrain_dyna_01", "terrain_dyna_02"} + +const ( + FILENAME_FL_INI = "freelancer.ini" +) + +func (r *Config) GetDlls() []string { + dlls := utils.CompL(r.Dlls, func(x *semantic.String) string { return x.Get() }) + return append([]string{"resources.dll"}, dlls...) +} + +type Config struct { + *iniload.IniLoader + + Dlls []*semantic.String + Markets []*semantic.Path + Goods []*semantic.Path + Equips []*semantic.Path + Universe []*semantic.Path + Ships []*semantic.Path + Loadouts []*semantic.Path +} + +func Read(input_file *iniload.IniLoader) *Config { + frelconfig := &Config{IniLoader: input_file} + + if resources, ok := input_file.SectionMap["[resources]"]; ok { + + for dll_index, _ := range resources[0].ParamMap[cfg.Key("dll")] { + frelconfig.Dlls = append(frelconfig.Dlls, + semantic.NewString(resources[0], cfg.Key("dll"), semantic.WithoutSpacesS(), semantic.OptsS(semantic.Index(dll_index))), + ) + } + } + + if resources, ok := input_file.SectionMap["[data]"]; ok { + for equipment_index, _ := range resources[0].ParamMap[cfg.Key("equipment")] { + frelconfig.Equips = append(frelconfig.Equips, + semantic.NewPath(resources[0], cfg.Key("equipment"), semantic.WithoutSpacesP(), semantic.WithLowercaseP(), semantic.OptsP(semantic.Index(equipment_index))), + ) + } + for index, _ := range resources[0].ParamMap[cfg.Key("loadouts")] { + frelconfig.Loadouts = append(frelconfig.Loadouts, + semantic.NewPath(resources[0], cfg.Key("loadouts"), semantic.WithoutSpacesP(), semantic.WithLowercaseP(), semantic.OptsP(semantic.Index(index))), + ) + } + for equipment_index, _ := range resources[0].ParamMap[cfg.Key("markets")] { + frelconfig.Markets = append(frelconfig.Markets, + semantic.NewPath(resources[0], cfg.Key("markets"), semantic.WithoutSpacesP(), semantic.WithLowercaseP(), semantic.OptsP(semantic.Index(equipment_index))), + ) + } + for equipment_index, _ := range resources[0].ParamMap[cfg.Key("universe")] { + frelconfig.Universe = append(frelconfig.Universe, + semantic.NewPath(resources[0], cfg.Key("universe"), semantic.WithoutSpacesP(), semantic.WithLowercaseP(), semantic.OptsP(semantic.Index(equipment_index))), + ) + } + for equipment_index, _ := range resources[0].ParamMap[cfg.Key("goods")] { + frelconfig.Goods = append(frelconfig.Goods, + semantic.NewPath(resources[0], cfg.Key("goods"), semantic.WithoutSpacesP(), semantic.WithLowercaseP(), semantic.OptsP(semantic.Index(equipment_index))), + ) + } + for equipment_index, _ := range resources[0].ParamMap[cfg.Key("ships")] { + frelconfig.Ships = append(frelconfig.Ships, + semantic.NewPath(resources[0], cfg.Key("ships"), semantic.WithoutSpacesP(), semantic.WithLowercaseP(), semantic.OptsP(semantic.Index(equipment_index))), + ) + } + } + + return frelconfig +} + +func (frelconfig *Config) Write() *file.File { + inifile := frelconfig.Render() + inifile.Write(inifile.File) + return inifile.File +} diff --git a/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/exe_mapped/go-binary-pack/.gitignore b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/exe_mapped/go-binary-pack/.gitignore new file mode 100644 index 0000000..58b6702 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/exe_mapped/go-binary-pack/.gitignore @@ -0,0 +1,73 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test +*.prof + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# External packages folder +vendor/ + +# Created by .ignore support plugin (hsz.mobi) +# IntelliJ project files +.idea +*.iml +out +gen + +# Sensitive or high-churn files: +.idea/dataSources/ +.idea/dataSources.ids +.idea/dataSources.xml +.idea/dataSources.local.xml +.idea/sqlDataSources.xml +.idea/dynamic.xml +.idea/uiDesigner.xml + +# Gradle: +.idea/gradle.xml +.idea/libraries + +# Mongo Explorer plugin: +.idea/mongoSettings.xml + +## File-based project format: +*.iws + +## Plugin-specific files: + +# IntelliJ +/out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties diff --git a/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/exe_mapped/go-binary-pack/.travis.yml b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/exe_mapped/go-binary-pack/.travis.yml new file mode 100644 index 0000000..1e1d568 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/exe_mapped/go-binary-pack/.travis.yml @@ -0,0 +1,4 @@ +language: golang +golang: 1.7.1 +script: + - "go test -v -race ./..." diff --git a/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/exe_mapped/go-binary-pack/LICENSE b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/exe_mapped/go-binary-pack/LICENSE new file mode 100644 index 0000000..5b3854d --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/exe_mapped/go-binary-pack/LICENSE @@ -0,0 +1,26 @@ +Copyright (c) 2017, Roman Kachanovsky +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of nor the names of its contributors may be used to + endorse or promote products derived from this software without specific + prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/exe_mapped/go-binary-pack/README.md b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/exe_mapped/go-binary-pack/README.md new file mode 100644 index 0000000..a7526fc --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/exe_mapped/go-binary-pack/README.md @@ -0,0 +1,53 @@ +# Go BinaryPack + + +BinaryPack is a simple Golang library which implements some functionality of Python's [struct](https://docs.python.org/2/library/struct.html) package. + +This one is forked from [BinaryPack](https://github.com/roman-kachanovsky/go-binary-pack) with more supported `pad byte` type + +**Format characters** + +Format characters (some characters like H have been reserved for future implementation of unsigned numbers): + +``` + ? - bool, packed size 1 byte + b - int8, packed size 1 bytes + B - uint8, packed size 1 bytes + h - int16, packed size 2 bytes + H - uint16, packed size 2 bytes + i, l - int32, packed size 4 bytes + I, L - int32, packed size 4 bytes + q - int64, packed size 8 bytes + Q - uint64, packed size 8 bytes + f - float32, packed size 4 bytes + d - float64, packed size 8 bytes + Ns - string, packed size N bytes, N is a number of runes to pack/unpack +``` + + +**Install** + +`go get github.com/roman-kachanovsky/go-binary-pack/binary-pack` + +**How to use** + +```go +// Prepare format (slice of strings) +format := []string{"I", "?", "d", "6s"} + +// Prepare values to pack +values := []interface{}{4, true, 3.14, "Golang"} + +// Create BinaryPack object +bp := new(BinaryPack) + +// Pack values to []byte +data, err := bp.Pack(format, values) + +// Unpack binary data to []interface{} +unpacked_values, err := bp.UnPack(format, data) + +// You can calculate size of expected binary data by format +size, err := bp.CalcSize(format) + +``` diff --git a/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/exe_mapped/go-binary-pack/VENDOR.md b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/exe_mapped/go-binary-pack/VENDOR.md new file mode 100644 index 0000000..1d84bca --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/exe_mapped/go-binary-pack/VENDOR.md @@ -0,0 +1,5 @@ +cloned from https://github.com/canhlinh/go-binary-pack + +previously was in go.mod as +github.com/canhlinh/go-binary-pack v0.0.0-20181203110405-72348cf47f32 h1:2frz5u51940fkPXzOlKhY/zL4FBtXerEdNEs2AEicMA= +github.com/canhlinh/go-binary-pack v0.0.0-20181203110405-72348cf47f32/go.mod h1:5oL/IK/HhX2kkOQK0V5dpmpFQET3dRgjS+4hfSjs+Pw= diff --git a/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/exe_mapped/go-binary-pack/binary_pack.go b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/exe_mapped/go-binary-pack/binary_pack.go new file mode 100644 index 0000000..8a895de --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/exe_mapped/go-binary-pack/binary_pack.go @@ -0,0 +1,309 @@ +// Copyright 2017 Roman Kachanovsky. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +/* + Package binary_pack performs conversions between some Go values represented as byte slices. + This can be used in handling binary data stored in files or from network connections, + among other sources. It uses format slices of strings as compact descriptions of the layout + of the Go structs. + + Format characters (some characters like H have been reserved for future implementation of unsigned numbers): + ? - bool, packed size 1 byte + b - int8, packed size 1 bytes + B - uint8, packed size 1 bytes + h - int16, packed size 2 bytes + H - uint16, packed size 2 bytes + i, l - int32, packed size 4 bytes + I, L - int32, packed size 4 bytes + q - int64, packed size 8 bytes + Q - uint64, packed size 8 bytes + f - float32, packed size 4 bytes + d - float64, packed size 8 bytes + Ns - string, packed size N bytes, N is a number of runes to pack/unpack + +*/ +package binarypack + +import ( + "bytes" + "encoding/binary" + "errors" + "fmt" + "strconv" + "strings" +) + +// BinaryPack presents a BinaryPack +type BinaryPack struct{} + +// New create a new BinaryPack +func New() *BinaryPack { + return &BinaryPack{} +} + +// Pack returns a byte slice containing the values of msg slice packed according to the given format. +// The items of msg slice must match the values required by the format exactly. +func (bp *BinaryPack) Pack(format []string, msg []interface{}) ([]byte, error) { + if len(format) > len(msg) { + return nil, errors.New("Format is longer than values to pack") + } + + res := []byte{} + + for i, f := range format { + switch f { + case "?": + castedV, ok := msg[i].(bool) + if !ok { + return nil, errors.New("Type of passed value doesn't match to expected '" + f + "' (bool)") + } + res = append(res, boolToBytes(castedV)...) + case "b", "B": + castedV, ok := msg[i].(int) + if !ok { + return nil, errors.New("Type of passed value doesn't match to expected '" + f + "' (int, 2 bytes)") + } + res = append(res, intToBytes(castedV, 1)...) + case "h", "H": + castedV, ok := msg[i].(int) + if !ok { + return nil, errors.New("Type of passed value doesn't match to expected '" + f + "' (int, 2 bytes)") + } + res = append(res, intToBytes(castedV, 2)...) + case "i", "I", "l", "L": + castedV, ok := msg[i].(int) + if !ok { + return nil, errors.New("Type of passed value doesn't match to expected '" + f + "' (int, 4 bytes)") + } + res = append(res, intToBytes(castedV, 4)...) + case "q", "Q": + castedV, ok := msg[i].(int) + if !ok { + return nil, errors.New("Type of passed value doesn't match to expected '" + f + "' (int, 8 bytes)") + } + res = append(res, intToBytes(castedV, 8)...) + case "f": + castedV, ok := msg[i].(float32) + if !ok { + return nil, errors.New("Type of passed value doesn't match to expected '" + f + "' (float32)") + } + res = append(res, float32ToBytes(castedV, 4)...) + case "d": + castedV, ok := msg[i].(float64) + if !ok { + return nil, errors.New("Type of passed value doesn't match to expected '" + f + "' (float64)") + } + res = append(res, float64ToBytes(castedV, 8)...) + default: + if strings.Contains(f, "s") { + castedV, ok := msg[i].(string) + if !ok { + return nil, errors.New("Type of passed value doesn't match to expected '" + f + "' (string)") + } + n, _ := strconv.Atoi(strings.TrimRight(f, "s")) + res = append(res, []byte(fmt.Sprintf("%s%s", + castedV, strings.Repeat("\x00", n-len(castedV))))...) + } else { + return nil, errors.New("Unexpected format token: '" + f + "'") + } + } + } + + return res, nil +} + +// UnPack the byte slice (presumably packed by Pack(format, msg)) according to the given format. +// The result is a []interface{} slice even if it contains exactly one item. +// The byte slice must contain not less the amount of data required by the format +// (len(msg) must more or equal CalcSize(format)). +func (bp *BinaryPack) UnPack(format []string, msg []byte) ([]interface{}, error) { + expectedByte, err := bp.CalcSize(format) + + if err != nil { + return nil, err + } + + if expectedByte > len(msg) { + return nil, errors.New("Expected size is bigger than actual size of message") + } + + res := []interface{}{} + + for _, f := range format { + switch f { + case "b": + res = append(res, bytesToInt(msg[:1])) + msg = msg[1:] + case "B": + res = append(res, bytesToUint(msg[:1])) + msg = msg[1:] + case "?": + res = append(res, bytesToBool(msg[:1])) + msg = msg[1:] + case "h": + res = append(res, bytesToInt(msg[:2])) + msg = msg[2:] + case "H": + res = append(res, bytesToUint(msg[:2])) + msg = msg[2:] + case "i", "l": + res = append(res, bytesToInt(msg[:4])) + msg = msg[4:] + case "I", "L": + res = append(res, bytesToUint(msg[:4])) + msg = msg[4:] + case "q": + res = append(res, bytesToInt(msg[:8])) + msg = msg[8:] + case "Q": + res = append(res, bytesToUint(msg[:8])) + msg = msg[8:] + case "f": + res = append(res, bytesToFloat32(msg[:4])) + msg = msg[4:] + case "d": + res = append(res, bytesToFloat64(msg[:8])) + msg = msg[8:] + default: + if strings.Contains(f, "s") { + n, _ := strconv.Atoi(strings.TrimRight(f, "s")) + res = append(res, string(msg[:n])) + msg = msg[n:] + } else { + return nil, errors.New("Unexpected format token: '" + f + "'") + } + } + } + + return res, nil +} + +// CalcSize Returns the size of the struct (and hence of the byte slice) corresponding to the given format. +func (bp *BinaryPack) CalcSize(format []string) (int, error) { + var size int + + for _, f := range format { + switch f { + case "b", "B": + size++ + case "?": + size++ + case "h", "H": + size += 2 + case "i", "I", "l", "L", "f": + size += 4 + case "q", "Q", "d": + size += 8 + default: + if strings.Contains(f, "s") { + n, _ := strconv.Atoi(strings.TrimRight(f, "s")) + size = size + n + } else { + return 0, errors.New("Unexpected format token: '" + f + "'") + } + } + } + + return size, nil +} + +func boolToBytes(x bool) []byte { + if x { + return intToBytes(1, 1) + } + return intToBytes(0, 1) +} + +func bytesToBool(b []byte) bool { + return bytesToInt(b) > 0 +} + +func intToBytes(n int, size int) []byte { + buf := bytes.NewBuffer([]byte{}) + + switch size { + case 1: + binary.Write(buf, binary.LittleEndian, int8(n)) + case 2: + binary.Write(buf, binary.LittleEndian, int16(n)) + case 4: + binary.Write(buf, binary.LittleEndian, int32(n)) + default: + binary.Write(buf, binary.LittleEndian, int64(n)) + } + + return buf.Bytes()[0:size] +} + +func bytesToInt(b []byte) int { + buf := bytes.NewBuffer(b) + + switch len(b) { + case 1: + var x int8 + binary.Read(buf, binary.LittleEndian, &x) + return int(x) + case 2: + var x int16 + binary.Read(buf, binary.LittleEndian, &x) + return int(x) + case 4: + var x int32 + binary.Read(buf, binary.LittleEndian, &x) + return int(x) + default: + var x int64 + binary.Read(buf, binary.LittleEndian, &x) + return int(x) + } +} + +func bytesToUint(b []byte) int { + buf := bytes.NewBuffer(b) + + switch len(b) { + case 1: + var x uint8 + binary.Read(buf, binary.LittleEndian, &x) + return int(x) + case 2: + var x uint16 + binary.Read(buf, binary.LittleEndian, &x) + return int(x) + case 4: + var x uint32 + binary.Read(buf, binary.LittleEndian, &x) + return int(x) + default: + var x uint64 + binary.Read(buf, binary.LittleEndian, &x) + return int(x) + } +} + +func float32ToBytes(n float32, size int) []byte { + buf := bytes.NewBuffer([]byte{}) + binary.Write(buf, binary.LittleEndian, n) + return buf.Bytes()[0:size] +} + +func bytesToFloat32(b []byte) float32 { + var x float32 + buf := bytes.NewBuffer(b) + binary.Read(buf, binary.LittleEndian, &x) + return x +} + +func float64ToBytes(n float64, size int) []byte { + buf := bytes.NewBuffer([]byte{}) + binary.Write(buf, binary.LittleEndian, n) + return buf.Bytes()[0:size] +} + +func bytesToFloat64(b []byte) float64 { + var x float64 + buf := bytes.NewBuffer(b) + binary.Read(buf, binary.LittleEndian, &x) + return x +} diff --git a/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/infocard_mapped/infocard/infotypes.go b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/infocard_mapped/infocard/infotypes.go new file mode 100644 index 0000000..b923cad --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/infocard_mapped/infocard/infotypes.go @@ -0,0 +1,31 @@ +package infocard + +type Infocard struct { + content string + Lines []string +} + +func NewInfocard(content string) *Infocard { + return &Infocard{content: content} +} + +type Infoname string + +type RecordKind string + +const ( + TYPE_NAME RecordKind = "NAME" + TYPE_INFOCAD RecordKind = "INFOCARD" +) + +type Config struct { + Infonames map[int]Infoname + Infocards map[int]*Infocard +} + +func NewConfig() *Config { + return &Config{ + Infocards: make(map[int]*Infocard), + Infonames: make(map[int]Infoname), + } +} diff --git a/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/infocard_mapped/infocard/xml.go b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/infocard_mapped/infocard/xml.go new file mode 100644 index 0000000..6aa367b --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/infocard_mapped/infocard/xml.go @@ -0,0 +1,48 @@ +package infocard + +import ( + "bytes" + "encoding/xml" + "io" + "strings" +) + +func (i *Infocard) XmlToText() ([]string, error) { + return XmlToText(i.content) +} + +func (i *Infocard) GetContent() string { + return i.content +} + +func XmlToText(raw string) ([]string, error) { + prepared := strings.ReplaceAll(string(raw), ``, "") + decoder := xml.NewDecoder(bytes.NewBufferString(prepared)) + + lines := make([]string, 0, 10) + var sb strings.Builder + for { + token, err := decoder.Token() + if err != nil { + if err == io.EOF { + break + } else { + return nil, err + } + } + + switch tok := token.(type) { + case xml.EndElement: + if tok.Name.Local == "PARA" || tok.Name.Local == "POP" { + lines = append(lines, sb.String()) + sb.Reset() + } + case xml.CharData: + sb.WriteString(string(tok)) + default: + continue + } + } + + return lines, nil +} diff --git a/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/infocard_mapped/infocards.go b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/infocard_mapped/infocards.go new file mode 100644 index 0000000..c962c71 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/infocard_mapped/infocards.go @@ -0,0 +1,148 @@ +package infocard_mapped + +import ( + "regexp" + "strconv" + "strings" + "sync" + + "github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/exe_mapped" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/infocard_mapped/infocard" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/filefind" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/filefind/file" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/inireader" + + "github.com/darklab8/fl-darkstat/configs/configs_settings/logus" + logus1 "github.com/darklab8/fl-darkstat/configs/configs_settings/logus" + "github.com/darklab8/go-typelog/typelog" +) + +const ( + FILENAME = "infocards.txt" + FILENAME_FALLBACK = "infocards.xml" +) + +/* For darklint */ +func ReadFromTextFile(input_file *file.File) (*infocard.Config, error) { + frelconfig := infocard.NewConfig() + + lines, err := input_file.ReadLines() + if logus.Log.CheckError(err, "unable to read file in ReadFromTestFile") { + return nil, err + } + + for index := 0; index < len(lines); index++ { + + id, err := strconv.Atoi(lines[index]) + if err != nil { + continue + } + name := lines[index+1] + content := lines[index+2] + index += 3 + + switch infocard.RecordKind(name) { + case infocard.TYPE_NAME: + frelconfig.Infonames[id] = infocard.Infoname(content) + case infocard.TYPE_INFOCAD: + frelconfig.Infocards[id] = infocard.NewInfocard(content) + default: + logus1.Log.Fatal( + "unrecognized object name in infocards.txt", + typelog.Any("id", id), + typelog.Any("name", name), + typelog.Any("content", content), + ) + } + } + return frelconfig, nil +} + +var InfocardReader *regexp.Regexp + +func init() { + inireader.InitRegexExpression(&InfocardReader, `^([0-9]+)[= ]+(.*)$`) +} + +func ReadFromDiscoServerConfig(input_file *file.File) (*infocard.Config, error) { + frelconfig := infocard.NewConfig() + + lines, err := input_file.ReadLines() + if logus.Log.CheckError(err, "unable to read file in ReadFromTestFile") { + return nil, err + } + + for _, line := range lines { + + line_match := InfocardReader.FindStringSubmatch(line) + if len(line_match) <= 0 { + continue + } + + id, err := strconv.Atoi(line_match[1]) + if err != nil { + continue + } + + content := line_match[2] + + var kind infocard.RecordKind = infocard.TYPE_NAME + if strings.Contains(content, " [{entry_name -> entry_values}]}""" + result = [] + string_table = {} + file_size = getsize(path) + + with open(path, 'rb') as f: + # read file header + magic, version, str_table_offset = unpack('4sII', f.read(12)) + assert magic == b'BINI', version == 1 + + # read string table, which stretches from str_table_offset to EOF + f.seek(str_table_offset) + raw_table = f.read(file_size - str_table_offset - 1) + + count = 0 + for s in raw_table.split(b'\0'): + string_table[count] = s.decode('cp1252').lower() + count += len(s) + 1 + + # return to end of header to read sections + f.seek(12) + + while f.tell() < str_table_offset: + # read section header + section_name_ptr, entry_count = unpack('hh', f.read(4)) + section_name = string_table[section_name_ptr] + + section_entries = [] + for e in range(entry_count): + # read entry + entry_name_ptr, value_count = unpack('hb', f.read(3)) + entry_name = string_table[entry_name_ptr] + entry_values = [] + + for v in range(value_count): + # read value + value_type, = unpack('b', f.read(1)) + value_data, = unpack(VALUE_TYPES[value_type], f.read(4)) + + if value_type == 3: + # it is a pointer relative to the string table + value_data = string_table[value_data] + + entry_values.append(value_data) + + if value_count > 1: + entry_value = tuple(entry_values) + elif value_count == 1: + entry_value = entry_values[0] + else: + continue + + section_entries.append((entry_name, entry_value)) + result.append((section_name, section_entries)) + + return tuple(result) + + +def dump(path: str) -> str: + """Dump the BINI file at `path` to an INI-formatted string.""" + bini = parse_file(path, fold_values=False) + + lines = [] + for section_name, sections in bini: + lines.append(f'[{section_name}]') + for entry in sections: + + key, values = entry + if not isinstance(values, tuple): + values = (values,) + + entries = f'{key} = {", ".join(list([str(value) for value in values]))}' + lines.append(entries) + lines.append('') # add a blank line after each section + return '\n'.join(lines) + + +def is_bini(path: str) -> bool: + """Returns whether the (.ini) file at `path` is a BINI by checking its magic number.""" + with open(path, 'rb') as f: + data = f.read(4) + return data[:4] == b'BINI' + + + +if __name__=="__main__": + # file = pathlib.Path(__file__).parent / "constants.vanilla.ini" + # data = dump(str(file)) + # print(f"{data=}") + + file2 = pathlib.Path(__file__).parent / "engine_good.ini" + data = dump(str(file2)) + print(f"{data=}") diff --git a/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/bini/requireqments.txt b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/bini/requireqments.txt new file mode 100644 index 0000000..8fa0fe5 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/bini/requireqments.txt @@ -0,0 +1,2 @@ +dataclassy>=0.8 +deconstruct>0.2 diff --git a/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/filefind/file/byte_file.go b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/filefind/file/byte_file.go new file mode 100644 index 0000000..b60e361 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/filefind/file/byte_file.go @@ -0,0 +1,30 @@ +package file + +import ( + "io" + "net/http" + "os" + + "github.com/darklab8/fl-darkstat/configs/configs_settings/logus" + "github.com/darklab8/go-typelog/typelog" +) + +func (f *File) ReadBytes() ([]byte, error) { + if f.webfile != nil { + res, err := http.Get(f.webfile.url) + if logus.Log.CheckError(err, "error making http request: %s\n", typelog.OptError(err)) { + return []byte{}, err + } + + resBody, err := io.ReadAll(res.Body) + if logus.Log.CheckError(err, "client: could not read response body: %s\n", typelog.OptError(err)) { + return []byte{}, err + } + + return resBody, nil + } + + resBody, err := os.ReadFile(f.filepath.ToString()) + logus.Log.Error("client: could not read os.ReadFile body: %s\n", typelog.OptError(err)) + return resBody, err +} diff --git a/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/filefind/file/file_handler.go b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/filefind/file/file_handler.go new file mode 100644 index 0000000..7fcbbe1 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/filefind/file/file_handler.go @@ -0,0 +1,143 @@ +/* +# File handling functions + +F in OpenToReadF stands for... Do succesfully, or log to Fatal level and exit +*/ +package file + +import ( + "bufio" + "fmt" + "io" + "net/http" + "os" + "strings" + + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/bini" + "github.com/darklab8/fl-darkstat/configs/configs_settings/logus" + "github.com/darklab8/go-typelog/typelog" + + "github.com/darklab8/go-utils/utils/utils_logus" + "github.com/darklab8/go-utils/utils/utils_types" +) + +type WebFile struct { + url string +} + +type File struct { + filepath utils_types.FilePath + file *os.File + lines []string + + // failback files escape fate of being written in + IsFailback bool + + webfile *WebFile +} + +func NewMemoryFile(lines []string) *File { + return &File{lines: lines} +} + +func NewFile(filepath utils_types.FilePath) *File { + return &File{filepath: filepath} +} + +func NewWebFile(url string) *File { + return &File{webfile: &WebFile{ + url: url, + }} +} + +func (f *File) GetFilepath() utils_types.FilePath { return f.filepath } + +func (f *File) openToReadF() *File { + logus.Log.Debug("opening file", utils_logus.FilePath(f.GetFilepath())) + file, err := os.Open(string(f.filepath)) + f.file = file + + logus.Log.CheckPanic(err, "failed to open ", utils_logus.FilePath(f.filepath)) + return f +} + +func (f *File) close() { + f.file.Close() +} + +func (f *File) ReadLines() ([]string, error) { + + if len(f.lines) > 0 { + lines := f.lines + f.lines = []string{} + return lines, nil + } + + if f.webfile != nil { + res, err := http.Get(f.webfile.url) + if err != nil { + logus.Log.Error("error making http request: %s\n", typelog.OptError(err)) + return []string{}, err + } + + resBody, err := io.ReadAll(res.Body) + if err != nil { + logus.Log.Error("client: could not read response body: %s\n", typelog.OptError(err)) + return []string{}, err + } + // fmt.Printf("client: response body: %s\n", resBody) + + str := string(resBody) + return strings.Split(str, "\n"), nil + } + + if bini.IsBini(f.filepath) { + bini_lines := bini.Dump(f.filepath) + return bini_lines, nil + } + + f.openToReadF() + defer f.close() + + scanner := bufio.NewScanner(f.file) + + bufio_lines := []string{} + for scanner.Scan() { + bufio_lines = append(bufio_lines, scanner.Text()) + } + return bufio_lines, nil +} + +func (f *File) ScheduleToWrite(value ...string) { + f.lines = append(f.lines, value...) +} + +func (f *File) WriteLines() { + if f.IsFailback { + // This feature is not working in full capacity for some reason ;) not getting skipped for some reason + logus.Log.Warn("file is taken from fallback, writing is skipped", + typelog.Any("filename", f.filepath.ToString()), + ) + return + } + + f.createToWriteF() + defer f.close() + + for _, line := range f.lines { + f.writelnF(line) + } +} + +func (f *File) createToWriteF() *File { + file, err := os.Create(string(f.filepath)) + f.file = file + logus.Log.CheckPanic(err, "failed to open ", utils_logus.FilePath(f.filepath)) + + return f +} +func (f *File) writelnF(msg string) { + _, err := f.file.WriteString(fmt.Sprintf("%v\n", msg)) + + logus.Log.CheckPanic(err, "failed to write string to file") +} diff --git a/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/filefind/filefind.go b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/filefind/filefind.go new file mode 100644 index 0000000..677aae2 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/filefind/filefind.go @@ -0,0 +1,91 @@ +/* +Package with reusable code for discovery of files and other reusable stuff like universal ini reader +*/ +package filefind + +import ( + "fmt" + "io/fs" + "path/filepath" + "strings" + + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/filefind/file" + "github.com/darklab8/fl-darkstat/configs/configs_settings" + "github.com/darklab8/fl-darkstat/configs/configs_settings/logus" + "github.com/darklab8/go-typelog/typelog" + + "github.com/darklab8/go-utils/utils/utils_logus" + "github.com/darklab8/go-utils/utils/utils_types" +) + +type Filesystem struct { + Files []*file.File + Hashmap map[utils_types.FilePath]*file.File +} + +var FreelancerFolder Filesystem + +func FindConfigs(folderpath utils_types.FilePath) *Filesystem { + var filesystem *Filesystem = &Filesystem{} + filesystem.Hashmap = make(map[utils_types.FilePath]*file.File) + + if configs_settings.Env.FreelancerFolderFailback != "" && folderpath != configs_settings.Env.FreelancerFolderFailback { + fs := FindConfigs(configs_settings.Env.FreelancerFolderFailback) + filesystem.Hashmap = fs.Hashmap + filesystem.Files = fs.Files + for _, file := range filesystem.Files { + file.IsFailback = true + } + for _, file := range filesystem.Hashmap { + file.IsFailback = true + } + } + + err := filepath.WalkDir(string(folderpath), func(path string, d fs.DirEntry, err error) error { + + // Disco dev files + if strings.Contains(path, "SERVICE") { + fmt.Println() + return nil + } + + if !strings.Contains(path, ".ini") && + !strings.Contains(path, ".txt") && + !strings.Contains(path, ".cfg") && + !strings.Contains(path, ".xml") && + !strings.Contains(path, ".dll") && + !strings.Contains(path, ".yml") && + !strings.Contains(path, ".json") { + return nil + } + + logus.Log.CheckPanic(err, "unable to read file") + + file := file.NewFile(utils_types.FilePath(path)) + filesystem.Files = append(filesystem.Files, file) + + key := utils_types.FilePath(strings.ToLower(filepath.Base(path))) + filesystem.Hashmap[key] = file + + return nil + }) + + logus.Log.CheckPanic(err, "unable to read files") + return filesystem +} + +func (file1system Filesystem) GetFile(file1names ...utils_types.FilePath) *file.File { + for _, file1name := range file1names { + file_, ok := file1system.Hashmap[file1name] + if !ok { + logus.Log.Warn("Filesystem.GetFile, failed to find find in filesystesm file trying to recover", utils_logus.FilePath(file1name)) + continue + } + logus.Log.Info("Filesystem.GetFile, found filepath=", utils_logus.FilePath(file_.GetFilepath())) + result_file := file.NewFile(file_.GetFilepath()) + return result_file + } + + logus.Log.Warn("failed to get file", typelog.Items[utils_types.FilePath]("filenames", file1names)) + return nil +} diff --git a/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/iniload/configfile.go b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/iniload/configfile.go new file mode 100644 index 0000000..535be08 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/iniload/configfile.go @@ -0,0 +1,31 @@ +package iniload + +import ( + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/filefind/file" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/inireader" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/semantic" + "github.com/darklab8/fl-darkstat/configs/configs_settings/logus" +) + +type IniLoader struct { + semantic.ConfigModel + input_file *file.File + *inireader.INIFile +} + +func NewLoader(input_file *file.File) *IniLoader { + fileconfig := &IniLoader{input_file: input_file} + return fileconfig +} + +// Scan is heavy operations for goroutine ^_^ +func (fileconfig *IniLoader) Scan() *IniLoader { + if fileconfig.input_file == nil { + logus.Log.Error("input_file is empty") + } + + iniconfig := inireader.Read(fileconfig.input_file) + fileconfig.Init(iniconfig.Sections, iniconfig.Comments, iniconfig.File.GetFilepath()) + fileconfig.INIFile = iniconfig + return fileconfig +} diff --git a/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/inireader/.gitignore b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/inireader/.gitignore new file mode 100644 index 0000000..e7392b0 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/inireader/.gitignore @@ -0,0 +1 @@ +*_rendered.ini diff --git a/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/inireader/inireader.go b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/inireader/inireader.go new file mode 100644 index 0000000..c898717 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/inireader/inireader.go @@ -0,0 +1,431 @@ +/* +Okay we need to create syntax. To augment currently possible stuff +*/ +package inireader + +import ( + "fmt" + "regexp" + "strconv" + "strings" + + "github.com/darklab8/fl-darkstat/configs/cfg" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/filefind/file" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/inireader/inireader_types" + "github.com/darklab8/go-typelog/typelog" + "github.com/darklab8/go-utils/utils/utils_logus" + + "github.com/darklab8/fl-darkstat/configs/configs_settings/logus" +) + +type INIFile struct { + File *file.File + Comments []string + + Sections []*Section + + // denormalization + SectionMap map[inireader_types.IniHeader][]*Section + SectionMapByNick map[string]*Section +} + +func (config *INIFile) AddSection(key inireader_types.IniHeader, section *Section) { + config.Sections = append(config.Sections, section) + + // Denormalization adding to hashmap + if config.SectionMap == nil { + config.SectionMap = make(map[inireader_types.IniHeader][]*Section) + } + if _, ok := config.SectionMap[key]; !ok { + config.SectionMap[key] = make([]*Section, 0) + } + config.SectionMap[key] = append(config.SectionMap[key], section) +} + +/* +[BaseGood] // this is Type +abc = 123 // this is Param going into list and hashmap +*/ +type Section struct { + OriginalType inireader_types.IniHeader // with preserved case sesntivity + Type inireader_types.IniHeader // to lower case + Params []*Param + // denormialization of Param list due to being more comfortable + ParamMap map[cfg.ParamKey][]*Param + + INIFile *INIFile + Comment string +} + +func (s Section) ToString(with_comments WithComments) string { + var sb strings.Builder + sb.WriteString(string(s.OriginalType)) + + if bool(with_comments) && s.Comment != "" { + sb.WriteString(fmt.Sprintf(" ; %s", s.Comment)) + } + + return sb.String() +} + +const ( + OPTIONAL_p = true + REQUIRED_p = false +) + +func (section *Section) AddParam(key cfg.ParamKey, param *Param) { + param.Key = key + + section.Params = append(section.Params, param) + // Denormalization, adding to hashmap + if section.ParamMap == nil { + section.ParamMap = make(map[cfg.ParamKey][]*Param) + } + if _, ok := section.ParamMap[key]; !ok { + section.ParamMap[key] = make([]*Param, 0) + } + section.ParamMap[key] = append(section.ParamMap[key], param) +} + +func (section *Section) AddParamToStart(key cfg.ParamKey, param *Param) { + param.Key = key + + section.Params = append([]*Param{param}, section.Params...) + // Denormalization, adding to hashmap + if section.ParamMap == nil { + section.ParamMap = make(map[cfg.ParamKey][]*Param) + } + if _, ok := section.ParamMap[key]; !ok { + section.ParamMap[key] = make([]*Param, 0) + } + section.ParamMap[key] = append(section.ParamMap[key], param) +} + +func (section *Section) GetParamStr(key cfg.ParamKey, optional bool) string { + if optional && len(section.ParamMap[key]) == 0 { + return "" + } + return section.ParamMap[key][0].First.AsString() +} +func (section *Section) GetParamStrToLower(key cfg.ParamKey, optional bool) string { + return strings.ToLower(section.GetParamStr(key, optional)) +} +func (section *Section) GetParamInt(key cfg.ParamKey, optional bool) int { + if optional && len(section.ParamMap[key]) == 0 { + return 0 + } + + integer, err := strconv.Atoi(section.GetParamStr(key, false)) + if err != nil { + logus.Log.Fatal("failed to parse strid in universe.ini", + typelog.Any("key", key), + typelog.Any("section", section)) + } + return integer +} +func (section *Section) GetParamNumber(key_v string, optional bool) ValueNumber { + key := cfg.Key(key_v) + if optional && len(section.ParamMap[key]) == 0 { + return ValueNumber{} + } + + return section.ParamMap[key][0].First.(ValueNumber) +} +func (section *Section) GetParamBool(key_v string, optional bool, default_value bool) bool { + key := cfg.Key(key_v) + if optional && len(section.ParamMap[key]) == 0 { + return default_value + } + + bool_value, err := strconv.ParseBool(section.GetParamStr(key, REQUIRED_p)) + + if err != nil { + return default_value + } + + return bool_value +} + +// abc = qwe, 1, 2, 3, 4 +// abc is key +// qwe is first value +// qwe, 1, 2, 3, 4 are values +// ;abc = qwe, 1, 2, 3 is Comment +type Param struct { + Key cfg.ParamKey + Values []UniValue + IsParamAsComment bool // if special param as comment for autogenerated comments + First UniValue // denormalization due to very often being needed + Comment string +} + +func (p *Param) AddValue(value UniValue) *Param { + if len(p.Values) == 0 { + p.First = value + } + p.Values = append(p.Values, value) + return p +} + +type WithComments bool + +func (p Param) ToString(with_comments WithComments) string { + + if p.IsParamAsComment && p.First.AsString() == "" { + return "" + } + + if p.Key == KEY_COMMENT { + return fmt.Sprintf(";%s", string(p.First.(ValueString))) + } + + var sb strings.Builder + + if p.IsParamAsComment { + sb.WriteString(";%") + } + + sb.WriteString(fmt.Sprintf("%v = ", p.Key)) + + for index, value := range p.Values { + str_to_write := value.AsString() + if index == len(p.Values)-1 { + sb.WriteString(str_to_write) + } else { + sb.WriteString(fmt.Sprintf("%v, ", str_to_write)) + } + } + + if bool(with_comments) && p.Comment != "" { + sb.WriteString(fmt.Sprintf(" ; %s", p.Comment)) + } + + return sb.String() +} + +type UniValue interface { + AsString() string +} +type ValueString string +type ValueNumber struct { + Value float64 + Precision int +} + +type ValueBool bool + +func (v ValueBool) AsString() string { + return strconv.FormatBool(bool(v)) +} + +func (v ValueString) AsString() string { + return string(v) +} + +func (v ValueString) ToLowerValue() ValueString { + return ValueString(strings.ToLower(string(v))) +} + +func (v ValueNumber) AsString() string { + return strconv.FormatFloat(float64(v.Value), 'f', v.Precision, 64) +} + +func UniParse(input string) (UniValue, error) { + letterMatch := regexLetter.FindAllString(input, -1) + if len(letterMatch) == 0 { + input = strings.ReplaceAll(input, " ", "") + } + + numberMatch := regexNumber.FindAllString(input, -1) + if len(numberMatch) > 0 { + parsed_number, err := strconv.ParseFloat(input, 64) + + if err != nil { + logus.Log.Warn("failed to read number. Converting to string", typelog.Any("input", input)) + return ValueString(input), nil + } + + var precision int + + if !strings.Contains(input, ".") { + precision = 0 + } else { + split := strings.Split(input, ".") + precision = len(split[1]) + } + + return ValueNumber{Value: parsed_number, Precision: precision}, nil + } + + v := ValueString(input) + return v, nil +} +func UniParseF(input string) UniValue { + value, err := UniParse(input) + if err != nil { + logus.Log.Fatal("unable to parse UniParseF", typelog.Any("input", input)) + } + return value +} + +func UniParseStr(input string) UniValue { + return ValueString(input) +} + +func UniParseInt(input int) UniValue { + u := ValueNumber{} + u.Value = float64(input) + u.Precision = 0 + return u +} + +func UniParseFloat(input float64, precision int) UniValue { + u := ValueNumber{} + u.Value = float64(input) + u.Precision = precision + return u +} + +var regexNumber *regexp.Regexp +var regexComment *regexp.Regexp +var regexSection *regexp.Regexp + +const ( + optionalComment = `(?:(?:[ ]*;[ ]*)(.+))?` + regexSectionRegExp = `^\x{FEFF}?(\[.*\])` + optionalComment + parameterExp = `(;%|^)[ ]*([a-zA-Z_][a-zA-Z_0-9]+)\s*=\s*([#+a-zA-Z_, 0-9-.\/\\]+)` + optionalComment +) + +var regexParam *regexp.Regexp +var regexLetter *regexp.Regexp + +func init() { + InitRegexExpression(®exNumber, `^[0-9\-]+(?:\.)?(?:e)?(?:\+)?([0-9\-]*)(?:E[-0-9]+)?$`) + InitRegexExpression(®exComment, `;(.*)`) // (?:[ ]+)?(?:;)?(?:[ ]+)?(.*) + InitRegexExpression(®exSection, regexSectionRegExp) + InitRegexExpression(®exLetter, `[a-zA-Z]`) + // param or commented out param + InitRegexExpression(®exParam, parameterExp) +} + +var CASE_SENSETIVE_KEYS = [...]string{"BGCS_base_run_by", "NavMapScale"} + +func isKeyCaseSensetive(key string) bool { + for _, sensetive_key := range CASE_SENSETIVE_KEYS { + if key == sensetive_key { + return true + } + } + return false +} + +func Read(fileref *file.File) *INIFile { + logus.Log.Debug("started reading INIFileRead for", utils_logus.FilePath(fileref.GetFilepath())) + config := &INIFile{} + config.File = fileref + + logus.Log.Debug("reading lines") + lines, err := fileref.ReadLines() + + if logus.Log.CheckError(err, "unable to read ini with error", typelog.OptError(err)) { + return config + } + + logus.Log.Debug("setting current section") + var cur_section *Section + for _, line := range lines { + comment_match := regexComment.FindStringSubmatch(line) + section_match := regexSection.FindStringSubmatch(line) + param_match := regexParam.FindStringSubmatch(line) + + if len(param_match) > 0 { + isComment := len(param_match[1]) > 0 + key := param_match[2] + if !isKeyCaseSensetive(key) { + key = strings.ToLower(key) + } + + line_to_read := param_match[3] + if strings.Contains(line_to_read, ",") { + line_to_read = strings.ReplaceAll(line_to_read, " ", "") + } + splitted_values := strings.Split(line_to_read, ",") + for i, _ := range splitted_values { + splitted_values[i] = strings.TrimSpace(splitted_values[i]) + } + var first_value UniValue + + var values []UniValue + if len(splitted_values) < 1 { + panic("expected some splitted values, as formerly accessed splitted_values[0] here") + } + for index, value := range splitted_values { + univalue, err := UniParse(value) + logus.Log.CheckFatal(err, "ini reader, failing to parse line because of UniParse #2, line="+line, utils_logus.FilePath(fileref.GetFilepath())) + + if index == 0 { + first_value = univalue + } + + values = append(values, univalue) + } + + param := Param{Key: cfg.Key(key), First: first_value, Values: values, IsParamAsComment: isComment, Comment: param_match[4]} + cur_section.AddParam(cfg.Key(key), ¶m) + } else if len(section_match) > 0 { + cur_section = &Section{ + INIFile: config, + Comment: section_match[2], + } // create new + cur_section.OriginalType = inireader_types.IniHeader(section_match[1]) + cur_section.Type = inireader_types.IniHeader(strings.ToLower(string(cur_section.OriginalType))) + config.AddSection(cur_section.Type, cur_section) + } else if len(comment_match) > 0 || line == "" { + var comment_value string + if len(comment_match) >= 2 { + comment_value = comment_match[1] + } + if cur_section == nil { + config.Comments = append(config.Comments, comment_value) + } else { + comment := UniParseStr(comment_value) + cur_section.AddParam(KEY_COMMENT, &Param{Key: KEY_COMMENT, First: comment, Values: []UniValue{comment}, IsParamAsComment: true}) + } + } + + } + + config.SectionMapByNick = make(map[string]*Section) + for _, section := range config.Sections { + if value, ok := section.ParamMap[cfg.Key("nickname")]; ok { + nickname := value[0].First.AsString() + config.SectionMapByNick[nickname] = section + } + } + + return config +} + +var KEY_COMMENT cfg.ParamKey = cfg.Key("00e0fc91e00300ed") // random hash + +func (config INIFile) Write(fileref *file.File) *file.File { + + for _, comment := range config.Comments { + if comment == "" { + fileref.ScheduleToWrite("") + continue + } + fileref.ScheduleToWrite(fmt.Sprintf(";%s", comment)) + } + + for _, section := range config.Sections { + fileref.ScheduleToWrite(section.ToString(WithComments(true))) + + for _, param := range section.Params { + fileref.ScheduleToWrite(param.ToString(WithComments(true))) + } + + } + + return fileref +} diff --git a/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/inireader/inireader_types/types.go b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/inireader/inireader_types/types.go new file mode 100644 index 0000000..ca67e72 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/inireader/inireader_types/types.go @@ -0,0 +1,7 @@ +package inireader_types + +type IniHeader string + +type IniKey string + +type IniValue string diff --git a/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/inireader/regexep.go b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/inireader/regexep.go new file mode 100644 index 0000000..32d6632 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/inireader/regexep.go @@ -0,0 +1,17 @@ +package inireader + +import ( + "regexp" + + "github.com/darklab8/fl-darkstat/configs/configs_settings/logus" + + "github.com/darklab8/go-utils/utils/utils_logus" + "github.com/darklab8/go-utils/utils/utils_os" +) + +func InitRegexExpression(regex **regexp.Regexp, expression string) { + var err error + + *regex, err = regexp.Compile(expression) + logus.Log.CheckPanic(err, "failed to parse numberParser in ", utils_logus.FilePath(utils_os.GetCurrentFile())) +} diff --git a/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/semantic/kind_bool.go b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/semantic/kind_bool.go new file mode 100644 index 0000000..1b6bff3 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/semantic/kind_bool.go @@ -0,0 +1,107 @@ +package semantic + +import ( + "strings" + + "github.com/darklab8/fl-darkstat/configs/cfg" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/inireader" + "github.com/darklab8/fl-darkstat/configs/configs_settings/logus" + "github.com/darklab8/go-typelog/typelog" +) + +type Bool struct { + *Value + bool_type BoolType +} + +type BoolType int64 + +const ( + IntBool BoolType = iota + StrBool +) + +func NewBool(section *inireader.Section, key cfg.ParamKey, bool_type BoolType, opts ...ValueOption) *Bool { + v := NewValue(section, key) + for _, opt := range opts { + opt(v) + } + s := &Bool{ + Value: v, + bool_type: bool_type, + } + + return s +} + +func (s *Bool) get() bool { + if s.optional && len(s.section.ParamMap[s.key]) == 0 { + return false + } + switch s.bool_type { + case IntBool: + return int(s.section.ParamMap[s.key][s.index].Values[s.order].(inireader.ValueNumber).Value) == 1 + case StrBool: + return strings.Contains(s.section.ParamMap[s.key][s.index].Values[s.order].AsString(), "true") + } + panic("not expected bool type") +} + +func (s *Bool) Get() bool { + defer handleGetCrashReporting(s.Value) + return s.get() +} + +func (s *Bool) GetValue() (bool, bool) { + var value bool + var ok bool = true + func() { + defer func() { + if r := recover(); r != nil { + logus.Log.Debug("Recovered from int GetValue Error:\n", typelog.Any("recover", r)) + ok = false + } + }() + value = s.get() + }() + + return value, ok +} + +func (s *Bool) Set(value bool) { + var processed_value inireader.UniValue + if s.isComment() { + s.Delete() + } + + switch s.bool_type { + case IntBool: + var int_bool int + if value { + int_bool = 1 + } + processed_value = inireader.UniParseInt(int_bool) + case StrBool: + if value { + processed_value = inireader.UniParseStr("true") + } else { + processed_value = inireader.UniParseStr("false") + } + } + + if len(s.section.ParamMap[s.key]) == 0 { + s.section.AddParamToStart(s.key, (&inireader.Param{IsParamAsComment: s.isComment()}).AddValue(processed_value)) + } + // implement SetValue in Section + s.section.ParamMap[s.key][0].First = processed_value + s.section.ParamMap[s.key][0].Values[0] = processed_value +} + +func (s *Bool) Delete() { + delete(s.section.ParamMap, s.key) + for index, param := range s.section.Params { + if param.Key == s.key { + s.section.Params = append(s.section.Params[:index], s.section.Params[index+1:]...) + } + } +} diff --git a/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/semantic/kind_float.go b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/semantic/kind_float.go new file mode 100644 index 0000000..5d3f89a --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/semantic/kind_float.go @@ -0,0 +1,104 @@ +package semantic + +import ( + "github.com/darklab8/fl-darkstat/configs/cfg" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/inireader" + "github.com/darklab8/fl-darkstat/configs/configs_settings/logus" + "github.com/darklab8/go-typelog/typelog" +) + +type Precision int + +type Float struct { + *Value + precision Precision + default_value float64 +} + +type FloatOption func(s *Float) + +func WithDefaultF(default_value float64) FloatOption { + return func(s *Float) { s.default_value = default_value } +} + +func OptsF(opts ...ValueOption) FloatOption { + return func(s *Float) { + for _, opt := range opts { + opt(s.Value) + } + } +} + +func NewFloat(section *inireader.Section, key cfg.ParamKey, precision Precision, opts ...FloatOption) *Float { + v := NewValue(section, key) + + s := &Float{ + Value: v, + precision: precision, + } + for _, opt := range opts { + opt(s) + } + return s +} + +func (s *Float) get() float64 { + var section *inireader.Section = s.section + if _, ok := s.section.ParamMap[s.key]; !ok { + if inherit_value, ok := s.section.ParamMap[InheritKey]; ok { + inherit_nick := inherit_value[0].First.AsString() + if found_section, ok := s.section.INIFile.SectionMapByNick[inherit_nick]; ok { + section = found_section + } + } + } + + if s.optional && len(section.ParamMap[s.key]) == 0 { + return 0 + } + return section.ParamMap[s.key][s.index].Values[s.order].(inireader.ValueNumber).Value +} + +func (s *Float) Get() float64 { + defer handleGetCrashReporting(s.Value) + return s.get() +} + +func (s *Float) GetValue() (float64, bool) { + var value float64 = s.default_value + var ok bool = true + func() { + defer func() { + if r := recover(); r != nil { + logus.Log.Debug("Recovered from int GetValue Error:\n", typelog.Any("recover", r)) + ok = false + } + }() + value = s.get() + }() + + return value, ok +} + +func (s *Float) Set(value float64) { + if s.isComment() { + s.Delete() + } + + processed_value := inireader.UniParseFloat(value, int(s.precision)) + if len(s.section.ParamMap[s.key]) == 0 { + s.section.AddParamToStart(s.key, (&inireader.Param{IsParamAsComment: s.isComment()}).AddValue(processed_value)) + } + // implement SetValue in Section + s.section.ParamMap[s.key][0].First = processed_value + s.section.ParamMap[s.key][0].Values[0] = processed_value +} + +func (s *Float) Delete() { + delete(s.section.ParamMap, s.key) + for index, param := range s.section.Params { + if param.Key == s.key { + s.section.Params = append(s.section.Params[:index], s.section.Params[index+1:]...) + } + } +} diff --git a/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/semantic/kind_int.go b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/semantic/kind_int.go new file mode 100644 index 0000000..1bca407 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/semantic/kind_int.go @@ -0,0 +1,85 @@ +package semantic + +import ( + "github.com/darklab8/fl-darkstat/configs/cfg" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/inireader" + "github.com/darklab8/fl-darkstat/configs/configs_settings/logus" + "github.com/darklab8/go-typelog/typelog" +) + +type Int struct { + *Value +} + +func NewInt(section *inireader.Section, key cfg.ParamKey, opts ...ValueOption) *Int { + v := NewValue(section, key) + for _, opt := range opts { + opt(v) + } + s := &Int{Value: v} + + return s +} + +var InheritKey = cfg.Key("inherit") + +func (s *Int) get() int { + var section *inireader.Section = s.section + if _, ok := s.section.ParamMap[s.key]; !ok { + if inherit_value, ok := s.section.ParamMap[InheritKey]; ok { + inherit_nick := inherit_value[0].First.AsString() + if found_section, ok := s.section.INIFile.SectionMapByNick[inherit_nick]; ok { + section = found_section + } + } + } + + if s.optional && len(section.ParamMap[s.key]) == 0 { + return 0 + } + return int(section.ParamMap[s.key][s.index].Values[s.order].(inireader.ValueNumber).Value) +} + +func (s *Int) Get() int { + defer handleGetCrashReporting(s.Value) + return s.get() +} + +func (s *Int) GetValue() (int, bool) { + var value int + var ok bool = true + func() { + defer func() { + if r := recover(); r != nil { + logus.Log.Debug("Recovered from int GetValue Error:\n", typelog.Any("recover", r)) + ok = false + } + }() + value = s.get() + }() + + return value, ok +} + +func (s *Int) Set(value int) { + if s.isComment() { + s.Delete() + } + + processed_value := inireader.UniParseInt(value) + if len(s.section.ParamMap[s.key]) == 0 { + s.section.AddParamToStart(s.key, (&inireader.Param{IsParamAsComment: s.isComment()}).AddValue(processed_value)) + } + // implement SetValue in Section + s.section.ParamMap[s.key][0].First = processed_value + s.section.ParamMap[s.key][0].Values[0] = processed_value +} + +func (s *Int) Delete() { + delete(s.section.ParamMap, s.key) + for index, param := range s.section.Params { + if param.Key == s.key { + s.section.Params = append(s.section.Params[:index], s.section.Params[index+1:]...) + } + } +} diff --git a/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/semantic/kind_path.go b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/semantic/kind_path.go new file mode 100644 index 0000000..002e765 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/semantic/kind_path.go @@ -0,0 +1,121 @@ +package semantic + +import ( + "path/filepath" + "strings" + + "github.com/darklab8/fl-darkstat/configs/cfg" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/inireader" + "github.com/darklab8/fl-darkstat/configs/configs_settings/logus" + "github.com/darklab8/go-typelog/typelog" + "github.com/darklab8/go-utils/utils/utils_types" +) + +// Linux friendly filepath, that can be returned to Windows way from linux +type Path struct { + *Value + remove_spaces bool + lowercase bool +} + +type PathOption func(s *Path) + +func WithoutSpacesP() PathOption { + return func(s *Path) { s.remove_spaces = true } +} + +func WithLowercaseP() PathOption { + return func(s *Path) { s.lowercase = true } +} + +func OptsP(opts ...ValueOption) PathOption { + return func(s *Path) { + for _, opt := range opts { + opt(s.Value) + } + } +} + +func NewPath(section *inireader.Section, key cfg.ParamKey, opts ...PathOption) *Path { + v := NewValue(section, key) + s := &Path{Value: v} + for _, opt := range opts { + opt(s) + } + return s +} + +func (s *Path) FileName() utils_types.FilePath { + if s.optional && len(s.section.ParamMap[s.key]) == 0 { + return "" + } + value := s.section.ParamMap[s.key][s.index].Values[s.order].AsString() + value = strings.ReplaceAll(value, "\\", PATH_SEPARATOR) + value = filepath.Base(value) + if s.remove_spaces { + value = strings.ReplaceAll(value, " ", "") + } + if s.lowercase { + value = strings.ToLower(value) + } + return utils_types.FilePath(value) +} + +func (s *Path) get() utils_types.FilePath { + if s.optional && len(s.section.ParamMap[s.key]) == 0 { + return "" + } + value := s.section.ParamMap[s.key][s.index].Values[s.order].AsString() + value = strings.ReplaceAll(value, "\\", PATH_SEPARATOR) + if s.remove_spaces { + value = strings.ReplaceAll(value, " ", "") + } + if s.lowercase { + value = strings.ToLower(value) + } + return utils_types.FilePath(value) +} + +func (s *Path) Get() utils_types.FilePath { + defer handleGetCrashReporting(s.Value) + return s.get() +} + +func (s *Path) GetValue() (utils_types.FilePath, bool) { + var value utils_types.FilePath + var ok bool = true + func() { + defer func() { + if r := recover(); r != nil { + logus.Log.Debug("Recovered from int GetValue Error:\n", typelog.Any("recover", r)) + ok = false + } + }() + value = s.get() + }() + + return value, ok +} + +func (s *Path) Set(value utils_types.FilePath) { + if s.isComment() { + s.Delete() + } + + processed_value := inireader.UniParseStr(string(value)) + if len(s.section.ParamMap[s.key]) == 0 { + s.section.AddParamToStart(s.key, (&inireader.Param{IsParamAsComment: s.isComment()}).AddValue(processed_value)) + } + // implement SetValue in Section + s.section.ParamMap[s.key][0].First = processed_value + s.section.ParamMap[s.key][0].Values[0] = processed_value +} + +func (s *Path) Delete() { + delete(s.section.ParamMap, s.key) + for index, param := range s.section.Params { + if param.Key == s.key { + s.section.Params = append(s.section.Params[:index], s.section.Params[index+1:]...) + } + } +} diff --git a/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/semantic/kind_string.go b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/semantic/kind_string.go new file mode 100644 index 0000000..77781fc --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/semantic/kind_string.go @@ -0,0 +1,114 @@ +package semantic + +import ( + "strings" + + "github.com/darklab8/fl-darkstat/configs/cfg" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/inireader" + "github.com/darklab8/fl-darkstat/configs/configs_settings/logus" + "github.com/darklab8/go-typelog/typelog" +) + +type String struct { + *Value + remove_spaces bool + lowercase bool +} + +type StringOption func(s *String) + +func WithoutSpacesS() StringOption { + return func(s *String) { s.remove_spaces = true } +} + +func WithLowercaseS() StringOption { + return func(s *String) { s.lowercase = true } +} + +func OptsS(opts ...ValueOption) StringOption { + return func(s *String) { + for _, opt := range opts { + opt(s.Value) + } + } +} + +func NewString(section *inireader.Section, key cfg.ParamKey, opts ...StringOption) *String { + v := NewValue(section, key) + s := &String{Value: v} + + for _, opt := range opts { + opt(s) + } + return s +} + +func (s *String) get() string { + var section *inireader.Section = s.section + if _, ok := s.section.ParamMap[s.key]; !ok { + if inherit_value, ok := s.section.ParamMap[InheritKey]; ok { + inherit_nick := inherit_value[0].First.AsString() + if found_section, ok := s.section.INIFile.SectionMapByNick[inherit_nick]; ok { + section = found_section + } + } + } + + if s.optional && len(section.ParamMap[s.key]) == 0 { + return "" + } + value := section.ParamMap[s.key][s.index].Values[s.order].AsString() + if s.remove_spaces { + value = strings.ReplaceAll(value, " ", "") + } + if s.lowercase { + value = strings.ToLower(value) + } + return value + +} + +func (s *String) Get() string { + + defer handleGetCrashReporting(s.Value) + return s.get() +} + +func (s *String) GetValue() (string, bool) { + var value string + var ok bool = true + func() { + defer func() { + if r := recover(); r != nil { + logus.Log.Debug("Recovered from int GetValue Error:\n", typelog.Any("recover", r)) + ok = false + } + }() + value = s.get() + }() + + return value, ok +} + +func (s *String) Set(value string) { + if s.isComment() { + s.Delete() + } + + processed_value := inireader.UniParseStr(value) + if len(s.section.ParamMap[s.key]) == 0 { + s.section.AddParamToStart(s.key, (&inireader.Param{IsParamAsComment: s.isComment()}).AddValue(processed_value)) + } + // implement SetValue in Section + s.section.ParamMap[s.key][0].First = processed_value + s.section.ParamMap[s.key][0].Values[0] = processed_value +} + +func (s *String) Delete() { + delete(s.section.ParamMap, s.key) + for index, param := range s.section.Params { + if param.Key == s.key { + s.section.Params = append(s.section.Params[:index], s.section.Params[index+1:]...) + } + } +} diff --git a/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/semantic/model.go b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/semantic/model.go new file mode 100644 index 0000000..54eb733 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/semantic/model.go @@ -0,0 +1,44 @@ +package semantic + +import ( + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/filefind/file" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/inireader" + + "github.com/darklab8/go-utils/utils/utils_types" +) + +type Model struct { + section *inireader.Section +} + +func (s *Model) Map(section *inireader.Section) { + s.section = section +} + +func (s *Model) RenderModel() *inireader.Section { + return s.section +} + +type ConfigModel struct { + sections []*inireader.Section + comments []string + filepath utils_types.FilePath +} + +func (s *ConfigModel) Init(sections []*inireader.Section, comments []string, filepath utils_types.FilePath) { + s.sections = sections + s.comments = comments + s.filepath = filepath +} + +func (s *ConfigModel) SetOutputPath(filepath utils_types.FilePath) { + s.filepath = filepath +} + +func (s *ConfigModel) Render() *inireader.INIFile { + inifile := &inireader.INIFile{} + inifile.Comments = s.comments + inifile.Sections = s.sections + inifile.File = file.NewFile(s.filepath) + return inifile +} diff --git a/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/semantic/path_unix.go b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/semantic/path_unix.go new file mode 100644 index 0000000..1799140 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/semantic/path_unix.go @@ -0,0 +1,5 @@ +//go:build !windows + +package semantic + +const PATH_SEPARATOR = `/` diff --git a/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/semantic/path_windows.go b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/semantic/path_windows.go new file mode 100644 index 0000000..9296b72 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/semantic/path_windows.go @@ -0,0 +1,5 @@ +//go:build windows + +package semantic + +const PATH_SEPARATOR = `\\` diff --git a/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/semantic/values.go b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/semantic/values.go new file mode 100644 index 0000000..25dbead --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/semantic/values.go @@ -0,0 +1,104 @@ +/* +ORM mapper for Freelancer ini reader. Easy mapping values to change. +*/ +package semantic + +import ( + "encoding/json" + "fmt" + "strings" + + "github.com/darklab8/fl-darkstat/configs/cfg" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/inireader" + "github.com/darklab8/fl-darkstat/configs/configs_settings/logus" + "github.com/darklab8/go-typelog/typelog" +) + +// ORM values + +type ValueType int64 + +const ( + TypeComment ValueType = iota + TypeVisible +) + +type Value struct { + section *inireader.Section + key cfg.ParamKey + optional bool + value_type ValueType + order int + index int +} + +func NewValue( + section *inireader.Section, + key cfg.ParamKey, +) *Value { + return &Value{ + section: section, + key: key, + value_type: TypeVisible, + } +} + +func (v Value) isComment() bool { + return v.value_type == TypeComment +} + +type ValueOption func(i *Value) + +func Order(order int) ValueOption { + return func(i *Value) { + i.order = order + } +} + +func Index(index int) ValueOption { + return func(i *Value) { + i.index = index + } +} + +func Optional() ValueOption { + return func(i *Value) { + i.optional = true + } +} + +func Comment() ValueOption { + return func(i *Value) { + i.value_type = TypeComment + } +} + +func quickJson(value any) string { + result, err := json.Marshal(value) + if err != nil { + return err.Error() + } + return string(result) +} + +func handleGetCrashReporting(value *Value) { + if r := recover(); r != nil { + if value == nil { + logus.Log.Panic("value is not defined. not possible. ;)") + return + } else { + var section strings.Builder + section.WriteString(string(value.section.Type)) + for _, param := range value.section.Params { + section.WriteString(fmt.Sprintf("\"%s\"", param.ToString(inireader.WithComments(true)))) + } + logus.Log.Error("unable to Get() from semantic.", + typelog.Any("value", quickJson(value)), + typelog.Any("key", value.key), + typelog.String("section", section.String()), + typelog.Any("r", r), + ) + } + panic(r) + } +} diff --git a/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/semantic/vector.go b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/semantic/vector.go new file mode 100644 index 0000000..430a34c --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/semantic/vector.go @@ -0,0 +1,49 @@ +package semantic + +import ( + "github.com/darklab8/fl-darkstat/configs/cfg" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/inireader" + "github.com/darklab8/fl-darkstat/configs/configs_settings/logus" + "github.com/darklab8/go-typelog/typelog" +) + +type Vect struct { + Model + X *Float + Y *Float + Z *Float +} + +func NewVector(section *inireader.Section, key cfg.ParamKey, precision Precision, opts ...ValueOption) *Vect { + v := &Vect{ + X: NewFloat(section, key, precision, OptsF(Order(0))), + Y: NewFloat(section, key, precision, OptsF(Order(1))), + Z: NewFloat(section, key, precision, OptsF(Order(2))), + } + v.Map(section) + return v +} + +func (s *Vect) Get() cfg.Vector { + return cfg.Vector{ + X: s.X.Get(), + Y: s.Y.Get(), + Z: s.Z.Get(), + } +} + +func (s *Vect) GetValue() (cfg.Vector, bool) { + var value cfg.Vector + var ok bool = true + func() { + defer func() { + if r := recover(); r != nil { + logus.Log.Debug("Recovered from int GetValue Error:\n", typelog.Any("recover", r)) + ok = false + } + }() + value = s.Get() + }() + + return value, ok +} diff --git a/vendor/github.com/darklab8/fl-darkstat/configs/configs_settings/logus/logus.go b/vendor/github.com/darklab8/fl-darkstat/configs/configs_settings/logus/logus.go new file mode 100644 index 0000000..85121c4 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/configs/configs_settings/logus/logus.go @@ -0,0 +1,8 @@ +package logus + +import ( + _ "github.com/darklab8/fl-darkstat/configs/configs_settings" + "github.com/darklab8/go-typelog/typelog" +) + +var Log *typelog.Logger = typelog.NewLogger("configs") diff --git a/vendor/github.com/darklab8/fl-darkstat/configs/configs_settings/settings.go b/vendor/github.com/darklab8/fl-darkstat/configs/configs_settings/settings.go new file mode 100644 index 0000000..6f3854d --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/configs/configs_settings/settings.go @@ -0,0 +1,64 @@ +package configs_settings + +import ( + "os" + "runtime" + + "github.com/darklab8/go-utils/utils/enverant" + "github.com/darklab8/go-utils/utils/utils_settings" + "github.com/darklab8/go-utils/utils/utils_types" +) + +type ConfEnvVars struct { + utils_settings.UtilsEnvs + FallbackInfonamesToNickname bool + Strict bool + FreelancerFolder utils_types.FilePath + FreelancerFolderFailback utils_types.FilePath + MaxCores *int +} + +var Env ConfEnvVars + +func init() { + Env = GetEnvs(enverant.NewEnverant()) +} + +func GetEnvs(envs *enverant.Enverant) ConfEnvVars { + Env = ConfEnvVars{ + UtilsEnvs: utils_settings.GetEnvs(envs), + FallbackInfonamesToNickname: envs.GetBool("CONFIGS_FALLBACK_TO_NICKNAMES", enverant.OrBool(false)), + Strict: envs.GetBool("CONFIGS_STRICT", enverant.OrBool(true)), + FreelancerFolder: getGameLocation(envs), + FreelancerFolderFailback: utils_types.FilePath(envs.GetStrOr("FREELANCER_FOLDER_FAILBACK", "")), + MaxCores: envs.GetPtrInt("CONFIGS_MAX_CORES"), + } + + return Env +} + +func GetMaxCores() int { + numCPUs := runtime.NumCPU() + if Env.MaxCores == nil { + return numCPUs + } + + if *Env.MaxCores > numCPUs { + return numCPUs + } + + return *Env.MaxCores +} + +func getGameLocation(envs *enverant.Enverant) utils_types.FilePath { + var folder utils_types.FilePath = utils_types.FilePath( + envs.GetStr("FREELANCER_FOLDER", enverant.OrStr("")), + ) + + if folder == "" { + workdir, _ := os.Getwd() + folder = utils_types.FilePath(workdir) + } + + return folder +} diff --git a/vendor/github.com/darklab8/fl-darkstat/configs/discovery/base_recipe_items/base_recipe_items.go b/vendor/github.com/darklab8/fl-darkstat/configs/discovery/base_recipe_items/base_recipe_items.go new file mode 100644 index 0000000..c730528 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/configs/discovery/base_recipe_items/base_recipe_items.go @@ -0,0 +1,70 @@ +package base_recipe_items + +import ( + "github.com/darklab8/fl-darkstat/configs/cfg" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/filefind/file" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/iniload" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/semantic" +) + +type CommodityRecipe struct { + semantic.Model + Nickname *semantic.String + ProcucedItem []*semantic.String + ConsumedItem []*semantic.String +} + +type Config struct { + *iniload.IniLoader + Recipes []*CommodityRecipe + RecipePerConsumed map[string][]*CommodityRecipe + RecipePerProduced map[string][]*CommodityRecipe +} + +func Read(input_file *iniload.IniLoader) *Config { + conf := &Config{ + IniLoader: input_file, + RecipePerConsumed: make(map[string][]*CommodityRecipe), + RecipePerProduced: make(map[string][]*CommodityRecipe), + } + + for _, recipe_info := range input_file.SectionMap["[recipe]"] { + + recipe := &CommodityRecipe{ + Nickname: semantic.NewString(recipe_info, cfg.Key("nickname"), semantic.WithLowercaseS(), semantic.WithoutSpacesS()), + } + recipe.Map(recipe_info) + + for produced_index, _ := range recipe_info.ParamMap[cfg.Key("produced_item")] { + + recipe.ProcucedItem = append(recipe.ProcucedItem, + semantic.NewString(recipe_info, cfg.Key("produced_item"), semantic.WithLowercaseS(), semantic.WithoutSpacesS(), semantic.OptsS(semantic.Index(produced_index)))) + } + for produced_index, produced_affiliation_info := range recipe_info.ParamMap[cfg.Key("produced_affiliation")] { + for i := 0; i < len(produced_affiliation_info.Values); i += 3 { + recipe.ProcucedItem = append(recipe.ProcucedItem, + semantic.NewString(recipe_info, cfg.Key("produced_affiliation"), semantic.WithLowercaseS(), semantic.WithoutSpacesS(), semantic.OptsS(semantic.Index(produced_index), semantic.Order(i)))) + } + } + + for consumed_index, _ := range recipe_info.ParamMap[cfg.Key("consumed")] { + + recipe.ConsumedItem = append(recipe.ConsumedItem, + semantic.NewString(recipe_info, cfg.Key("consumed"), semantic.WithLowercaseS(), semantic.WithoutSpacesS(), semantic.OptsS(semantic.Index(consumed_index)))) + + } + conf.Recipes = append(conf.Recipes, recipe) + for _, consumed := range recipe.ConsumedItem { + conf.RecipePerConsumed[consumed.Get()] = append(conf.RecipePerConsumed[consumed.Get()], recipe) + } + for _, produced := range recipe.ProcucedItem { + conf.RecipePerProduced[produced.Get()] = append(conf.RecipePerProduced[produced.Get()], recipe) + } + } + + return conf +} + +func (frelconfig *Config) Write() *file.File { + return &file.File{} +} diff --git a/vendor/github.com/darklab8/fl-darkstat/configs/discovery/discoprices/prices.go b/vendor/github.com/darklab8/fl-darkstat/configs/discovery/discoprices/prices.go new file mode 100644 index 0000000..b704759 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/configs/discovery/discoprices/prices.go @@ -0,0 +1,55 @@ +package discoprices + +import ( + "github.com/darklab8/fl-darkstat/configs/cfg" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/filefind/file" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/iniload" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/semantic" +) + +type Price struct { + semantic.Model + BaseNickname *semantic.String + CommodityNickname *semantic.String + PriceBaseBuysFor *semantic.Int + PriceBaseSellsFor *semantic.Int + BaseSells *semantic.Bool +} + +type Config struct { + *iniload.IniLoader + Prices []*Price + BasesPerGood map[string][]*Price + GoodsPerBase map[string][]*Price +} + +func Read(input_file *iniload.IniLoader) *Config { + conf := &Config{ + IniLoader: input_file, + BasesPerGood: make(map[string][]*Price), + GoodsPerBase: make(map[string][]*Price), + } + + for _, price_info := range input_file.SectionMap["[price]"] { + + marketgood_key := cfg.Key("marketgood") + for mg_index, _ := range price_info.ParamMap[marketgood_key] { + market_good := &Price{} + market_good.Map(price_info) + market_good.BaseNickname = semantic.NewString(price_info, marketgood_key, semantic.WithLowercaseS(), semantic.WithoutSpacesS(), semantic.OptsS(semantic.Index(mg_index), semantic.Order(0))) + market_good.CommodityNickname = semantic.NewString(price_info, marketgood_key, semantic.WithLowercaseS(), semantic.WithoutSpacesS(), semantic.OptsS(semantic.Index(mg_index), semantic.Order(1))) + market_good.PriceBaseBuysFor = semantic.NewInt(price_info, marketgood_key, semantic.Index(mg_index), semantic.Order(2)) + market_good.PriceBaseSellsFor = semantic.NewInt(price_info, marketgood_key, semantic.Index(mg_index), semantic.Order(3)) + market_good.BaseSells = semantic.NewBool(price_info, marketgood_key, semantic.IntBool, semantic.Index(mg_index), semantic.Order(4)) + conf.Prices = append(conf.Prices, market_good) + conf.BasesPerGood[market_good.CommodityNickname.Get()] = append(conf.BasesPerGood[market_good.CommodityNickname.Get()], market_good) + conf.GoodsPerBase[market_good.BaseNickname.Get()] = append(conf.GoodsPerBase[market_good.BaseNickname.Get()], market_good) + } + } + + return conf +} + +func (frelconfig *Config) Write() *file.File { + return &file.File{} +} diff --git a/vendor/github.com/darklab8/fl-darkstat/configs/discovery/playercntl_rephacks/id_rephacks.go b/vendor/github.com/darklab8/fl-darkstat/configs/discovery/playercntl_rephacks/id_rephacks.go new file mode 100644 index 0000000..0f0412e --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/configs/discovery/playercntl_rephacks/id_rephacks.go @@ -0,0 +1,117 @@ +package playercntl_rephacks + +import ( + "github.com/darklab8/fl-darkstat/configs/cfg" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/iniload" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/inireader" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/semantic" +) + +type RepType int + +func (r RepType) ToStr() string { + switch r { + case MODE_REP_LESSTHAN: + return "<= Maximum possible rep (not greater than)" + case MODE_REP_GREATERTHAN: + return ">= Minimum possible rep (not less than)" + case MODE_REP_NO_CHANGE: + return "? Not identified, MODE_REP_NO_CHANGE" + case MODE_REP_STATIC: + return "= Fixed value rep (forced static rep)" + } + return "undefined" +} + +const ( + // COPIED FROM https://github.com/Aingar/FLHook/blob/ca76a9fbfb74c5c5d609bd5042adca45a5ee866c/Plugins/Public/playercntl_plugin/RepFixer.cpp#L40 + + // The adjustment mode. If the player's reputation for scRepGroup + // is greater than fRep then make the reputation equal to fRep + MODE_REP_LESSTHAN RepType = 0 + + // The adjustment mode. If the player's reputation for scRepGroup + // is less than fRep then make the reputation equal to fRep + MODE_REP_GREATERTHAN RepType = 1 + + // Don't change anything/ignore this reputation group. + MODE_REP_NO_CHANGE RepType = 2 + + // Fix the rep group to this level. + MODE_REP_STATIC RepType = 3 +) + +type Faction struct { + semantic.Model + Nickname cfg.FactionNick + + Rep *semantic.Float + + // unknown: supposedly 0 means minimum or exact. 3 means maximum or exact. + RepType *semantic.Int +} + +func (f Faction) GetRepType() RepType { + return RepType(f.RepType.Get()) +} + +type Rephack struct { + semantic.Model + ID *semantic.String + Inherits *semantic.String + Reps map[cfg.FactionNick]Faction +} + +type Config struct { + DefaultReps map[cfg.FactionNick]Faction + + RephacksByID map[cfg.TractorID]Rephack +} + +func Read(input_file *iniload.IniLoader) *Config { + conf := &Config{ + DefaultReps: make(map[cfg.FactionNick]Faction), + RephacksByID: make(map[cfg.TractorID]Rephack), + } + + if resources, ok := input_file.SectionMap["[default_reps]"]; ok { + + default_reps := resources[0] + + for _, param := range default_reps.Params { + faction := Faction{ + Nickname: cfg.FactionNick(param.Key), + Rep: semantic.NewFloat(default_reps, param.Key, semantic.Precision(2)), + RepType: semantic.NewInt(default_reps, param.Key, semantic.Order(1)), + } + faction.Map(default_reps) + conf.DefaultReps[cfg.FactionNick(param.Key)] = faction + } + } + + for _, rephack_info := range input_file.SectionMap["[rephack]"] { + rephack := Rephack{ + ID: semantic.NewString(rephack_info, cfg.Key("id")), + Inherits: semantic.NewString(rephack_info, cfg.Key("inherits")), + Reps: make(map[cfg.FactionNick]Faction), + } + rephack.Map(rephack_info) + + for _, param := range rephack_info.Params { + if param.Key == cfg.Key("id") || param.Key == cfg.Key("inherits") || param.Key == inireader.KEY_COMMENT { + continue + } + + faction := Faction{ + Nickname: cfg.FactionNick(param.Key), + Rep: semantic.NewFloat(rephack_info, param.Key, semantic.Precision(2)), + RepType: semantic.NewInt(rephack_info, param.Key, semantic.Order(1)), + } + faction.Map(rephack_info) + rephack.Reps[cfg.FactionNick(param.Key)] = faction + } + + conf.RephacksByID[cfg.TractorID(rephack.ID.Get())] = rephack + } + return conf +} diff --git a/vendor/github.com/darklab8/fl-darkstat/configs/discovery/pob_goods/pob_goods.go b/vendor/github.com/darklab8/fl-darkstat/configs/discovery/pob_goods/pob_goods.go new file mode 100644 index 0000000..bb68994 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/configs/discovery/pob_goods/pob_goods.go @@ -0,0 +1,102 @@ +package pob_goods + +import ( + "encoding/json" + "html" + "regexp" + "strings" + + "github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/initialworld/flhash" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/filefind/file" + "github.com/darklab8/fl-darkstat/configs/configs_settings/logus" +) + +type ShopItem struct { + Id int `json:"id"` + Quantity int `json:"quantity"` + Price int `json:"price"` + SellPrice int `json:"sell_price"` + MinStock int `json:"min_stock"` + MaxStock int `json:"max_stock"` +} + +func (good ShopItem) BaseSells() bool { + return good.Quantity > good.MinStock +} +func (good ShopItem) BaseBuys() bool { + return good.Quantity < good.MaxStock +} + +type Base struct { + Name string + Nickname string + ShopItems []ShopItem `json:"shop_items"` + + ForumThreadUrl *string `json:"thread"` + CargoSpaceLeft *int `json:"cargospace"` + + SystemHash *flhash.HashCode `json:"system"` //: 2745655887, + Pos *string `json:"pos"` //: "299016, 33, -178", + AffiliationHash *flhash.HashCode `json:"affiliation"` //: 2620, + Level *int `json:"level"` //: 1, + Money *int `json:"money"` //: 0, + Health *float64 `json:"health"` //: 50, + DefenseMode *int `json:"defensemode"` //: 1, + InfocardParagraphs []string `json:"infocard_paragraphs"` + + HostileFactionHashList []*flhash.HashCode `json:"hostile_list"` + HostileTagList []string `json:"hostile_tag_list"` + HostileNameList []string `json:"hostile_name_list"` + AllyFactionHashList []*flhash.HashCode `json:"ally_list"` + AllyTagList []string `json:"ally_tag_list"` + AllyNameList []string `json:"ally_name_list"` + SrpFactionHashList []*flhash.HashCode `json:"srp_list"` + SrpTagList []string `json:"srp_tag_list"` + SrpNameList []string `json:"srp_name_list"` +} + +type Config struct { + file *file.File + BasesByName map[string]*Base `json:"bases"` + Timestamp string `json:"timestamp"` + Bases []*Base +} + +func (c *Config) Refresh() { + reread := Read(c.file) + c.file = reread.file + c.BasesByName = reread.BasesByName + c.Timestamp = reread.Timestamp + c.Bases = reread.Bases +} + +func NameToNickname(name string) string { + name = strings.ToLower(name) + name = html.UnescapeString(name) + name = regexp.MustCompile(`[^a-zA-Z0-9 ]+`).ReplaceAllString(name, "") + name = strings.ReplaceAll(name, " ", "_") + return name +} + +func Read(file *file.File) *Config { + byteValue, err := file.ReadBytes() + logus.Log.CheckFatal(err, "failed to read file") + + var conf *Config + json.Unmarshal(byteValue, &conf) + + for base_name, base := range conf.BasesByName { + base.Name = base_name + + hash := NameToNickname(base.Name) + base.Nickname = hash + conf.Bases = append(conf.Bases, base) + } + + conf.file = file + return conf +} + +func (frelconfig *Config) Write() *file.File { + return &file.File{} +} diff --git a/vendor/github.com/darklab8/fl-darkstat/configs/discovery/techcompat/techcompat.go b/vendor/github.com/darklab8/fl-darkstat/configs/discovery/techcompat/techcompat.go new file mode 100644 index 0000000..4e6a910 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/configs/discovery/techcompat/techcompat.go @@ -0,0 +1,184 @@ +package techcompat + +import ( + "github.com/darklab8/fl-darkstat/configs/cfg" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/filefind/file" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/iniload" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/semantic" +) + +type General struct { + semantic.Model + UnlistedTech *semantic.Float + DefaultMult *semantic.Float + NoControlItem *semantic.Float +} + +type TechCompatibility struct { + semantic.Model + Nickname *semantic.String + Percentage *semantic.Float +} + +type Faction struct { + semantic.Model + DefaultUnlisted *semantic.Float + ID *semantic.String + TechCompats []*TechCompatibility +} + +type TechGroup struct { + semantic.Model + Name *semantic.String + Default *semantic.Float + Items []*semantic.String +} + +type Config struct { + *iniload.IniLoader + General *General + Factions []*Faction + FactionByID map[cfg.TractorID]*Faction + TechGroups []*TechGroup + TechGroupByName map[string]*TechGroup + + // string ItemNickname + CompatByItem map[string]*ItemCompat +} + +type ItemCompat struct { + Default *float64 + TechCell string + CompatByID map[cfg.TractorID]float64 +} + +func (conf *Config) GetCompatibilty(item_nickname string, id_nickname cfg.TractorID) float64 { + + if id_nickname == "" { + // ; If the ship does not have a control item (in discovery this is the ID) then this + // ; multiplier is used. + return conf.General.NoControlItem.Get() + } + + faction, is_found_faction := conf.FactionByID[id_nickname] + + item, found_item := conf.CompatByItem[item_nickname] + if !found_item { + if is_found_faction { + if default_unlisted, ok := faction.DefaultUnlisted.GetValue(); ok { + return default_unlisted + } + } + // ; Any items not in a [tech] section use this multiplier. + // unlisted_tech = smth + return conf.General.UnlistedTech.Get() + } + + item_faction_compat, found_faction := item.CompatByID[id_nickname] + + if !found_faction { + // ; Anything in listed in a [tech] section but not an explicitly defined in the faction + // ; section combination uses this as the default multipier. + + if item.Default != nil { + return *item.Default + } else { + return conf.General.DefaultMult.Get() + } + } + + return item_faction_compat +} + +func Read(input_file *iniload.IniLoader) *Config { + conf := &Config{ + IniLoader: input_file, + FactionByID: make(map[cfg.TractorID]*Faction), + TechGroupByName: make(map[string]*TechGroup), + CompatByItem: make(map[string]*ItemCompat), + } + + if resources, ok := input_file.SectionMap["[general]"]; ok { + general_info := resources[0] + + conf.General = &General{ + UnlistedTech: semantic.NewFloat(general_info, cfg.Key("unlisted_tech"), semantic.Precision(2)), + DefaultMult: semantic.NewFloat(general_info, cfg.Key("default_mult"), semantic.Precision(2)), + NoControlItem: semantic.NewFloat(general_info, cfg.Key("no_control_item"), semantic.Precision(2)), + } + conf.General.Map(general_info) + + } + + for _, faction_info := range input_file.SectionMap["[faction]"] { + + faction_nicknames := faction_info.ParamMap[cfg.Key("item")][0] + for faction_order, _ := range faction_nicknames.Values { + faction := &Faction{ + DefaultUnlisted: semantic.NewFloat(faction_info, cfg.Key("default_unlisted"), semantic.Precision(2)), + } + faction.Map(faction_info) + faction.ID = semantic.NewString(faction_info, cfg.Key("item"), semantic.OptsS(semantic.Order(faction_order))) + + for index, _ := range faction_info.ParamMap[cfg.Key("tech")] { + compat := &TechCompatibility{} + compat.Map(faction_info) + compat.Nickname = semantic.NewString(faction_info, cfg.Key("tech"), semantic.OptsS(semantic.Index(index), semantic.Order(0))) + compat.Percentage = semantic.NewFloat(faction_info, cfg.Key("tech"), semantic.Precision(2), semantic.OptsF(semantic.Index(index), semantic.Order(1))) + faction.TechCompats = append(faction.TechCompats, compat) + } + + conf.Factions = append(conf.Factions, faction) + conf.FactionByID[cfg.TractorID(faction.ID.Get())] = faction + } + } + + for _, techgroup_info := range input_file.SectionMap["[tech]"] { + techgroup := &TechGroup{} + techgroup.Map(techgroup_info) + + techgroup.Name = semantic.NewString(techgroup_info, cfg.Key("name")) + techgroup.Default = semantic.NewFloat(techgroup_info, cfg.Key("default"), semantic.Precision(2)) + + for index, _ := range techgroup_info.ParamMap[cfg.Key("item")] { + techgroup.Items = append(techgroup.Items, semantic.NewString(techgroup_info, cfg.Key("item"), semantic.OptsS(semantic.Index(index)))) + } + + conf.TechGroups = append(conf.TechGroups, techgroup) + conf.TechGroupByName[techgroup.Name.Get()] = techgroup + + for _, item := range techgroup.Items { + item_nickname := item.Get() + compat, found_compat := conf.CompatByItem[item_nickname] + + if !found_compat { + compat = &ItemCompat{CompatByID: make(map[cfg.TractorID]float64)} + conf.CompatByItem[item_nickname] = compat + + if value, ok := techgroup.Default.GetValue(); ok { + compat.Default = &value + } + } + + compat.TechCell = techgroup.Name.Get() + + for _, faction := range conf.Factions { + for _, faction_compat := range faction.TechCompats { + if compat.TechCell != faction_compat.Nickname.Get() { + continue + } + + id_nickname := cfg.TractorID(faction.ID.Get()) + compat.CompatByID[id_nickname] = faction_compat.Percentage.Get() + + } + } + } + } + + return conf +} + +func (frelconfig *Config) Write() *file.File { + return &file.File{} +} diff --git a/vendor/github.com/darklab8/fl-darkstat/configs/overrides/overrides.go b/vendor/github.com/darklab8/fl-darkstat/configs/overrides/overrides.go new file mode 100644 index 0000000..5e48c88 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/configs/overrides/overrides.go @@ -0,0 +1,39 @@ +package overrides + +import ( + "os" + + "github.com/darklab8/fl-darkstat/configs/configs_settings/logus" + "github.com/darklab8/go-utils/utils/utils_types" + "gopkg.in/yaml.v3" +) + +const FILENAME = "overrides.fl_configs.yml" + +type Overrides struct { + SystemTravelSpeedMultipliers map[string]float64 `yaml:"system_travel_speed_multilpliers"` +} + +type InfocardRegion string + +func (o Overrides) GetSystemSpeedMultiplier(system_nickname string) float64 { + if value, ok := o.SystemTravelSpeedMultipliers[system_nickname]; ok { + return value + } else { + return 1.0 + } +} + +func Read(filepath utils_types.FilePath) Overrides { + var config Overrides + config.SystemTravelSpeedMultipliers = make(map[string]float64) + + data, err := os.ReadFile(filepath.ToString()) + logus.Log.CheckWarn(err, "overrides for fl configs is not found") + + err = yaml.Unmarshal(data, &config) + + logus.Log.CheckPanic(err, "failed inmarshaling yaml file for overrides") + + return config +} diff --git a/vendor/github.com/darklab8/fl-darkstat/configs/tests/fixtures.go b/vendor/github.com/darklab8/fl-darkstat/configs/tests/fixtures.go new file mode 100644 index 0000000..fa03bc7 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/configs/tests/fixtures.go @@ -0,0 +1,16 @@ +package tests + +import ( + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/filefind" + "github.com/darklab8/fl-darkstat/configs/configs_settings" +) + +var cached *filefind.Filesystem + +func FixtureFileFind() *filefind.Filesystem { + if cached != nil { + return cached + } + cached = filefind.FindConfigs(configs_settings.Env.FreelancerFolder) + return cached +} diff --git a/vendor/github.com/darklab8/fl-darkstat/darkcore/builder/builder.go b/vendor/github.com/darklab8/fl-darkstat/darkcore/builder/builder.go new file mode 100644 index 0000000..7a295d4 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/darkcore/builder/builder.go @@ -0,0 +1,130 @@ +package builder + +import ( + "fmt" + + "github.com/darklab8/fl-darkstat/darkcore/core_types" + "github.com/darklab8/go-utils/utils/timeit" + "github.com/darklab8/go-utils/utils/utils_filepath" + "github.com/darklab8/go-utils/utils/utils_types" +) + +type Builder struct { + components []*Component + params Params + static_files []StaticFile +} + +type StaticFile struct { + path utils_types.FilePath + content []byte +} + +func NewStaticFile(path utils_types.FilePath, content []byte) StaticFile { + return StaticFile{ + path: path, + content: content, + } +} + +func NewStaticFileFromCore(s core_types.StaticFile) StaticFile { + return NewStaticFile(utils_types.FilePath(s.Filename), []byte(s.Content)) +} + +type BuilderOption func(b *Builder) + +type Params interface { + GetBuildPath() utils_types.FilePath +} + +func NewBuilder(params Params, static_files []StaticFile, opts ...BuilderOption) *Builder { + b := &Builder{ + params: params, + static_files: static_files, + } + for _, opt := range opts { + opt(b) + } + return b +} + +func (b *Builder) RegComps(components ...*Component) { + b.components = append(b.components, components...) +} + +func chunkSlice(slice []*Component, chunkSize int) [][]*Component { + var chunks [][]*Component + for i := 0; i < len(slice); i += chunkSize { + end := i + chunkSize + + // necessary check to avoid slicing beyond + // slice capacity + if end > len(slice) { + end = len(slice) + } + + chunks = append(chunks, slice[i:end]) + } + + return chunks +} + +// func (b *Builder) ToWebServer() *Filesystem { +// } + +func (b *Builder) BuildAll(to_mem bool, filesystem *Filesystem) *Filesystem { + + build_root := utils_types.FilePath("build") + if filesystem == nil { + filesystem = NewFileystem(build_root) + } + + filesystem.CreateBuildFolder() + fmt.Println("beginning build operation") + results := make(chan WriteResult) + + timeit.NewTimerF(func() { + chunked_components := chunkSlice(b.components, 10000) + fmt.Println("components chunks", len(chunked_components)) + for chunk_index, components_chunk := range chunked_components { + + if to_mem { + for _, comp := range components_chunk { + filesystem.WriteToMem(comp.GetPagePath(b.params), &MemComp{ + comp: comp, + b: b, + }) + } + } else { + for _, comp := range components_chunk { + go func(comp *Component) { + results <- comp.Write(b.params) + }(comp) + } + for range components_chunk { + result := <-results + filesystem.WriteToFile(result.realpath, result.bytes) + } + } + + fmt.Println("finished chunk=", chunk_index) + } + + }, timeit.WithMsg("wrote components")) + + timeit.NewTimerF(func() { + target_folder := b.params.GetBuildPath().Join("static") + for _, static_file := range b.static_files { + path := utils_filepath.Join(target_folder, static_file.path) + if to_mem { + filesystem.WriteToMem(path, &MemStatic{ + content: static_file.content, + }) + } else { + filesystem.WriteToFile(path, []byte(static_file.content)) + } + } + }, timeit.WithMsg("gathered static assets")) + + return filesystem +} diff --git a/vendor/github.com/darklab8/fl-darkstat/darkcore/builder/component.go b/vendor/github.com/darklab8/fl-darkstat/darkcore/builder/component.go new file mode 100644 index 0000000..c7c4362 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/darkcore/builder/component.go @@ -0,0 +1,50 @@ +package builder + +import ( + "bytes" + "context" + + "github.com/darklab8/fl-darkstat/darkcore/core_types" + + "github.com/a-h/templ" + "github.com/darklab8/go-utils/utils/utils_filepath" + "github.com/darklab8/go-utils/utils/utils_types" +) + +type Component struct { + pagepath utils_types.FilePath + templ_comp templ.Component +} + +func NewComponent( + pagepath utils_types.FilePath, + templ_comp templ.Component, +) *Component { + return &Component{ + pagepath: pagepath, + templ_comp: templ_comp, + } +} + +type WriteResult struct { + realpath utils_types.FilePath + bytes []byte +} + +func (h *Component) GetPagePath(gp Params) utils_types.FilePath { + return utils_filepath.Join(gp.GetBuildPath(), h.pagepath) +} + +func (h *Component) Write(gp Params) WriteResult { + buf := bytes.NewBuffer([]byte{}) + + // gp.Pagepath = string(h.pagepath) + + h.templ_comp.Render(context.WithValue(context.Background(), core_types.GlobalParamsCtxKey, gp), buf) + + // Usage of gohtml is not obligatory, but nice touch simplifying debugging view. + return WriteResult{ + realpath: h.GetPagePath(gp), + bytes: buf.Bytes(), + } +} diff --git a/vendor/github.com/darklab8/fl-darkstat/darkcore/builder/fileystem.go b/vendor/github.com/darklab8/fl-darkstat/darkcore/builder/fileystem.go new file mode 100644 index 0000000..47eabd6 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/darkcore/builder/fileystem.go @@ -0,0 +1,88 @@ +package builder + +import ( + "os" + "path/filepath" + "sync" + + "github.com/darklab8/fl-darkstat/darkcore/settings/logus" + "github.com/darklab8/go-typelog/typelog" + "github.com/darklab8/go-utils/utils/utils_filepath" + "github.com/darklab8/go-utils/utils/utils_types" +) + +/* +Filesystem allows us to write to files to memory for later reusage in web app serving static assets from memory +Optionally same filesystem supports rendering to local, for deployment of static assets +*/ +type Filesystem struct { + Files map[utils_types.FilePath]MemFile + mu sync.Mutex + build_root utils_types.FilePath +} + +type MemFile interface { + Render() []byte +} + +type MemComp struct { + comp *Component + b *Builder +} + +func (m *MemComp) Render() []byte { + return m.comp.Write(m.b.params).bytes +} + +type MemStatic struct { + content []byte +} + +func (m *MemStatic) Render() []byte { + return m.content +} + +func NewFileystem(build_root utils_types.FilePath) *Filesystem { + b := &Filesystem{ + Files: make(map[utils_types.FilePath]MemFile), + build_root: build_root, + } + return b +} + +var PermReadWrite os.FileMode = 0666 + +func (f *Filesystem) GetBuildRoot() utils_types.FilePath { + return f.build_root +} + +func (f *Filesystem) WriteToMem(path utils_types.FilePath, content MemFile) { + f.mu.Lock() + f.Files[path] = content + f.mu.Unlock() +} + +func (f *Filesystem) WriteToFile(path utils_types.FilePath, content []byte) { + + final_path := utils_filepath.Join(f.build_root, path) + haveParentFoldersCreated(final_path) + // TODO add check for creating all missing folders in the path + err := os.WriteFile(final_path.ToString(), []byte(content), PermReadWrite) + logus.Log.CheckFatal(err, "failed to export bases to file") +} + +func (f *Filesystem) CreateBuildFolder() { + os.RemoveAll(f.build_root.ToString()) + os.MkdirAll(f.build_root.ToString(), os.ModePerm) +} + +func haveParentFoldersCreated(buildpath utils_types.FilePath) { + path := buildpath.ToString() + folder_path := filepath.Dir(path) + err := os.MkdirAll(folder_path, os.ModePerm) + logus.Log.CheckError(err, + "haveParentFoldersCreated finished", + typelog.String("folderpath", folder_path), + typelog.String("path", path), + ) +} diff --git a/vendor/github.com/darklab8/fl-darkstat/darkcore/core_front/core_front.go b/vendor/github.com/darklab8/fl-darkstat/darkcore/core_front/core_front.go new file mode 100644 index 0000000..497900d --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/darkcore/core_front/core_front.go @@ -0,0 +1,54 @@ +package core_front + +import ( + "embed" + + "github.com/darklab8/fl-darkstat/darkcore/core_types" + "github.com/darklab8/fl-darkstat/darkcore/settings/logus" + "github.com/darklab8/go-typelog/typelog" + "github.com/darklab8/go-utils/utils/utils_types" +) + +type StaticFilesystem struct { + Files []core_types.StaticFile + relPathToFile map[utils_types.FilePath]core_types.StaticFile +} + +func (fs StaticFilesystem) GetFileByRelPath(rel_path utils_types.FilePath) core_types.StaticFile { + file, ok := fs.relPathToFile[rel_path] + + if !ok { + logus.Log.Panic("expected file found by relpath", typelog.Any("relpath", rel_path)) + } + + return file +} + +func GetFiles(fs embed.FS, params utils_types.GetFilesParams) StaticFilesystem { + files := utils_types.GetFiles(fs, params) + var filesystem StaticFilesystem = StaticFilesystem{ + relPathToFile: make(map[utils_types.FilePath]core_types.StaticFile), + } + + for _, file := range files { + var static_file_kind core_types.StaticFileKind + + switch file.Extension { + case "js": + static_file_kind = core_types.StaticFileJS + case "css": + static_file_kind = core_types.StaticFileCSS + case "ico": + static_file_kind = core_types.StaticFileIco + } + + new_file := core_types.StaticFile{ + Filename: string(file.Relpath), + Kind: static_file_kind, + Content: string(file.Content), + } + filesystem.Files = append(filesystem.Files, new_file) + filesystem.relPathToFile[file.Relpath] = new_file + } + return filesystem +} diff --git a/vendor/github.com/darklab8/fl-darkstat/darkcore/core_front/example.templ b/vendor/github.com/darklab8/fl-darkstat/darkcore/core_front/example.templ new file mode 100644 index 0000000..cf1e7f3 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/darkcore/core_front/example.templ @@ -0,0 +1,15 @@ +package core_front + +import "github.com/darklab8/fl-darkstat/darkcore/core_types" + +templ StaticFile(file core_types.StaticFile) { + switch file.Kind { + case core_types.StaticFileCSS: + + // @templ.Raw("") + case core_types.StaticFileJS: + + case core_types.StaticFileIco: + + } +} diff --git a/vendor/github.com/darklab8/fl-darkstat/darkcore/core_static/favicon.ico b/vendor/github.com/darklab8/fl-darkstat/darkcore/core_static/favicon.ico new file mode 100644 index 0000000..cf54702 Binary files /dev/null and b/vendor/github.com/darklab8/fl-darkstat/darkcore/core_static/favicon.ico differ diff --git a/vendor/github.com/darklab8/fl-darkstat/darkcore/core_static/htmx.1.9.11.js b/vendor/github.com/darklab8/fl-darkstat/darkcore/core_static/htmx.1.9.11.js new file mode 100644 index 0000000..91a8d77 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/darkcore/core_static/htmx.1.9.11.js @@ -0,0 +1,3922 @@ +// UMD insanity +// This code sets up support for (in order) AMD, ES6 modules, and globals. +(function (root, factory) { + //@ts-ignore + if (typeof define === 'function' && define.amd) { + // AMD. Register as an anonymous module. + //@ts-ignore + define([], factory); + } else if (typeof module === 'object' && module.exports) { + // Node. Does not work with strict CommonJS, but + // only CommonJS-like environments that support module.exports, + // like Node. + module.exports = factory(); + } else { + // Browser globals + root.htmx = root.htmx || factory(); + } +}(typeof self !== 'undefined' ? self : this, function () { + return (function () { + 'use strict'; + + // Public API + //** @type {import("./htmx").HtmxApi} */ + // TODO: list all methods in public API + var htmx = { + onLoad: onLoadHelper, + process: processNode, + on: addEventListenerImpl, + off: removeEventListenerImpl, + trigger: triggerEvent, + ajax: ajaxHelper, + find: find, + findAll: findAll, + closest: closest, + values: function (elt, type) { + var inputValues = getInputValues(elt, type || "post"); + return inputValues.values; + }, + remove: removeElement, + addClass: addClassToElement, + removeClass: removeClassFromElement, + toggleClass: toggleClassOnElement, + takeClass: takeClassForElement, + defineExtension: defineExtension, + removeExtension: removeExtension, + logAll: logAll, + logNone: logNone, + logger: null, + config: { + historyEnabled: true, + historyCacheSize: 10, + refreshOnHistoryMiss: false, + defaultSwapStyle: 'innerHTML', + defaultSwapDelay: 0, + defaultSettleDelay: 20, + includeIndicatorStyles: true, + indicatorClass: 'htmx-indicator', + requestClass: 'htmx-request', + addedClass: 'htmx-added', + settlingClass: 'htmx-settling', + swappingClass: 'htmx-swapping', + allowEval: true, + allowScriptTags: true, + inlineScriptNonce: '', + attributesToSettle: ["class", "style", "width", "height"], + withCredentials: false, + timeout: 0, + wsReconnectDelay: 'full-jitter', + wsBinaryType: 'blob', + disableSelector: "[hx-disable], [data-hx-disable]", + useTemplateFragments: false, + scrollBehavior: 'smooth', + defaultFocusScroll: false, + getCacheBusterParam: false, + globalViewTransitions: false, + methodsThatUseUrlParams: ["get"], + selfRequestsOnly: false, + ignoreTitle: false, + scrollIntoViewOnBoost: true, + triggerSpecsCache: null, + }, + parseInterval: parseInterval, + _: internalEval, + createEventSource: function (url) { + return new EventSource(url, { withCredentials: true }) + }, + createWebSocket: function (url) { + var sock = new WebSocket(url, []); + sock.binaryType = htmx.config.wsBinaryType; + return sock; + }, + version: "1.9.11" + }; + + /** @type {import("./htmx").HtmxInternalApi} */ + var internalAPI = { + addTriggerHandler: addTriggerHandler, + bodyContains: bodyContains, + canAccessLocalStorage: canAccessLocalStorage, + findThisElement: findThisElement, + filterValues: filterValues, + hasAttribute: hasAttribute, + getAttributeValue: getAttributeValue, + getClosestAttributeValue: getClosestAttributeValue, + getClosestMatch: getClosestMatch, + getExpressionVars: getExpressionVars, + getHeaders: getHeaders, + getInputValues: getInputValues, + getInternalData: getInternalData, + getSwapSpecification: getSwapSpecification, + getTriggerSpecs: getTriggerSpecs, + getTarget: getTarget, + makeFragment: makeFragment, + mergeObjects: mergeObjects, + makeSettleInfo: makeSettleInfo, + oobSwap: oobSwap, + querySelectorExt: querySelectorExt, + selectAndSwap: selectAndSwap, + settleImmediately: settleImmediately, + shouldCancel: shouldCancel, + triggerEvent: triggerEvent, + triggerErrorEvent: triggerErrorEvent, + withExtensions: withExtensions, + } + + var VERBS = ['get', 'post', 'put', 'delete', 'patch']; + var VERB_SELECTOR = VERBS.map(function (verb) { + return "[hx-" + verb + "], [data-hx-" + verb + "]" + }).join(", "); + + var HEAD_TAG_REGEX = makeTagRegEx('head'), + TITLE_TAG_REGEX = makeTagRegEx('title'), + SVG_TAGS_REGEX = makeTagRegEx('svg', true); + + //==================================================================== + // Utilities + //==================================================================== + + /** + * @param {string} tag + * @param {boolean} global + * @returns {RegExp} + */ + function makeTagRegEx(tag, global = false) { + return new RegExp(`<${tag}(\\s[^>]*>|>)([\\s\\S]*?)<\\/${tag}>`, + global ? 'gim' : 'im'); + } + + function parseInterval(str) { + if (str == undefined) { + return undefined; + } + + let interval = NaN; + if (str.slice(-2) == "ms") { + interval = parseFloat(str.slice(0, -2)); + } else if (str.slice(-1) == "s") { + interval = parseFloat(str.slice(0, -1)) * 1000; + } else if (str.slice(-1) == "m") { + interval = parseFloat(str.slice(0, -1)) * 1000 * 60; + } else { + interval = parseFloat(str); + } + return isNaN(interval) ? undefined : interval; + } + + /** + * @param {HTMLElement} elt + * @param {string} name + * @returns {(string | null)} + */ + function getRawAttribute(elt, name) { + return elt.getAttribute && elt.getAttribute(name); + } + + // resolve with both hx and data-hx prefixes + function hasAttribute(elt, qualifiedName) { + return elt.hasAttribute && (elt.hasAttribute(qualifiedName) || + elt.hasAttribute("data-" + qualifiedName)); + } + + /** + * + * @param {HTMLElement} elt + * @param {string} qualifiedName + * @returns {(string | null)} + */ + function getAttributeValue(elt, qualifiedName) { + return getRawAttribute(elt, qualifiedName) || getRawAttribute(elt, "data-" + qualifiedName); + } + + /** + * @param {HTMLElement} elt + * @returns {HTMLElement | null} + */ + function parentElt(elt) { + return elt.parentElement; + } + + /** + * @returns {Document} + */ + function getDocument() { + return document; + } + + /** + * @param {HTMLElement} elt + * @param {(e:HTMLElement) => boolean} condition + * @returns {HTMLElement | null} + */ + function getClosestMatch(elt, condition) { + while (elt && !condition(elt)) { + elt = parentElt(elt); + } + + return elt ? elt : null; + } + + function getAttributeValueWithDisinheritance(initialElement, ancestor, attributeName) { + var attributeValue = getAttributeValue(ancestor, attributeName); + var disinherit = getAttributeValue(ancestor, "hx-disinherit"); + if (initialElement !== ancestor && disinherit && (disinherit === "*" || disinherit.split(" ").indexOf(attributeName) >= 0)) { + return "unset"; + } else { + return attributeValue + } + } + + /** + * @param {HTMLElement} elt + * @param {string} attributeName + * @returns {string | null} + */ + function getClosestAttributeValue(elt, attributeName) { + var closestAttr = null; + getClosestMatch(elt, function (e) { + return closestAttr = getAttributeValueWithDisinheritance(elt, e, attributeName); + }); + if (closestAttr !== "unset") { + return closestAttr; + } + } + + /** + * @param {HTMLElement} elt + * @param {string} selector + * @returns {boolean} + */ + function matches(elt, selector) { + // @ts-ignore: non-standard properties for browser compatibility + // noinspection JSUnresolvedVariable + var matchesFunction = elt.matches || elt.matchesSelector || elt.msMatchesSelector || elt.mozMatchesSelector || elt.webkitMatchesSelector || elt.oMatchesSelector; + return matchesFunction && matchesFunction.call(elt, selector); + } + + /** + * @param {string} str + * @returns {string} + */ + function getStartTag(str) { + var tagMatcher = /<([a-z][^\/\0>\x20\t\r\n\f]*)/i + var match = tagMatcher.exec(str); + if (match) { + return match[1].toLowerCase(); + } else { + return ""; + } + } + + /** + * + * @param {string} resp + * @param {number} depth + * @returns {Element} + */ + function parseHTML(resp, depth) { + var parser = new DOMParser(); + var responseDoc = parser.parseFromString(resp, "text/html"); + + /** @type {Element} */ + var responseNode = responseDoc.body; + while (depth > 0) { + depth--; + // @ts-ignore + responseNode = responseNode.firstChild; + } + if (responseNode == null) { + // @ts-ignore + responseNode = getDocument().createDocumentFragment(); + } + return responseNode; + } + + function aFullPageResponse(resp) { + return /", 0); + // @ts-ignore type mismatch between DocumentFragment and Element. + // TODO: Are these close enough for htmx to use interchangeably? + var fragmentContent = fragment.querySelector('template').content; + if (htmx.config.allowScriptTags) { + // if there is a nonce set up, set it on the new script tags + forEach(fragmentContent.querySelectorAll("script"), function (script) { + if (htmx.config.inlineScriptNonce) { + script.nonce = htmx.config.inlineScriptNonce; + } + // mark as executed due to template insertion semantics on all browsers except firefox fml + script.htmxExecuted = navigator.userAgent.indexOf("Firefox") === -1; + }) + } else { + forEach(fragmentContent.querySelectorAll("script"), function (script) { + // remove all script tags if scripts are disabled + removeElement(script); + }) + } + return fragmentContent; + } + switch (startTag) { + case "thead": + case "tbody": + case "tfoot": + case "colgroup": + case "caption": + return parseHTML("" + content + "
", 1); + case "col": + return parseHTML("" + content + "
", 2); + case "tr": + return parseHTML("" + content + "
", 2); + case "td": + case "th": + return parseHTML("" + content + "
", 3); + case "script": + case "style": + return parseHTML("

" + content + "
", 1); + default: + return parseHTML(content, 0); + } + } + + /** + * @param {Function} func + */ + function maybeCall(func) { + if (func) { + func(); + } + } + + /** + * @param {any} o + * @param {string} type + * @returns + */ + function isType(o, type) { + return Object.prototype.toString.call(o) === "[object " + type + "]"; + } + + /** + * @param {*} o + * @returns {o is Function} + */ + function isFunction(o) { + return isType(o, "Function"); + } + + /** + * @param {*} o + * @returns {o is Object} + */ + function isRawObject(o) { + return isType(o, "Object"); + } + + /** + * getInternalData retrieves "private" data stored by htmx within an element + * @param {HTMLElement} elt + * @returns {*} + */ + function getInternalData(elt) { + var dataProp = 'htmx-internal-data'; + var data = elt[dataProp]; + if (!data) { + data = elt[dataProp] = {}; + } + return data; + } + + /** + * toArray converts an ArrayLike object into a real array. + * @param {ArrayLike} arr + * @returns {any[]} + */ + function toArray(arr) { + var returnArr = []; + if (arr) { + for (var i = 0; i < arr.length; i++) { + returnArr.push(arr[i]); + } + } + return returnArr + } + + function forEach(arr, func) { + if (arr) { + for (var i = 0; i < arr.length; i++) { + func(arr[i]); + } + } + } + + function isScrolledIntoView(el) { + var rect = el.getBoundingClientRect(); + var elemTop = rect.top; + var elemBottom = rect.bottom; + return elemTop < window.innerHeight && elemBottom >= 0; + } + + function bodyContains(elt) { + // IE Fix + if (elt.getRootNode && elt.getRootNode() instanceof window.ShadowRoot) { + return getDocument().body.contains(elt.getRootNode().host); + } else { + return getDocument().body.contains(elt); + } + } + + function splitOnWhitespace(trigger) { + return trigger.trim().split(/\s+/); + } + + /** + * mergeObjects takes all of the keys from + * obj2 and duplicates them into obj1 + * @param {Object} obj1 + * @param {Object} obj2 + * @returns {Object} + */ + function mergeObjects(obj1, obj2) { + for (var key in obj2) { + if (obj2.hasOwnProperty(key)) { + obj1[key] = obj2[key]; + } + } + return obj1; + } + + function parseJSON(jString) { + try { + return JSON.parse(jString); + } catch (error) { + logError(error); + return null; + } + } + + function canAccessLocalStorage() { + var test = 'htmx:localStorageTest'; + try { + localStorage.setItem(test, test); + localStorage.removeItem(test); + return true; + } catch (e) { + return false; + } + } + + function normalizePath(path) { + try { + var url = new URL(path); + if (url) { + path = url.pathname + url.search; + } + // remove trailing slash, unless index page + if (!(/^\/$/.test(path))) { + path = path.replace(/\/+$/, ''); + } + return path; + } catch (e) { + // be kind to IE11, which doesn't support URL() + return path; + } + } + + //========================================================================================== + // public API + //========================================================================================== + + function internalEval(str) { + return maybeEval(getDocument().body, function () { + return eval(str); + }); + } + + function onLoadHelper(callback) { + var value = htmx.on("htmx:load", function (evt) { + callback(evt.detail.elt); + }); + return value; + } + + function logAll() { + htmx.logger = function (elt, event, data) { + if (console) { + console.log(event, elt, data); + } + } + } + + function logNone() { + htmx.logger = null + } + + function find(eltOrSelector, selector) { + if (selector) { + return eltOrSelector.querySelector(selector); + } else { + return find(getDocument(), eltOrSelector); + } + } + + function findAll(eltOrSelector, selector) { + if (selector) { + return eltOrSelector.querySelectorAll(selector); + } else { + return findAll(getDocument(), eltOrSelector); + } + } + + function removeElement(elt, delay) { + elt = resolveTarget(elt); + if (delay) { + setTimeout(function () { + removeElement(elt); + elt = null; + }, delay); + } else { + elt.parentElement.removeChild(elt); + } + } + + function addClassToElement(elt, clazz, delay) { + elt = resolveTarget(elt); + if (delay) { + setTimeout(function () { + addClassToElement(elt, clazz); + elt = null; + }, delay); + } else { + elt.classList && elt.classList.add(clazz); + } + } + + function removeClassFromElement(elt, clazz, delay) { + elt = resolveTarget(elt); + if (delay) { + setTimeout(function () { + removeClassFromElement(elt, clazz); + elt = null; + }, delay); + } else { + if (elt.classList) { + elt.classList.remove(clazz); + // if there are no classes left, remove the class attribute + if (elt.classList.length === 0) { + elt.removeAttribute("class"); + } + } + } + } + + function toggleClassOnElement(elt, clazz) { + elt = resolveTarget(elt); + elt.classList.toggle(clazz); + } + + function takeClassForElement(elt, clazz) { + elt = resolveTarget(elt); + forEach(elt.parentElement.children, function (child) { + removeClassFromElement(child, clazz); + }) + addClassToElement(elt, clazz); + } + + function closest(elt, selector) { + elt = resolveTarget(elt); + if (elt.closest) { + return elt.closest(selector); + } else { + // TODO remove when IE goes away + do { + if (elt == null || matches(elt, selector)) { + return elt; + } + } + while (elt = elt && parentElt(elt)); + return null; + } + } + + function startsWith(str, prefix) { + return str.substring(0, prefix.length) === prefix + } + + function endsWith(str, suffix) { + return str.substring(str.length - suffix.length) === suffix + } + + function normalizeSelector(selector) { + var trimmedSelector = selector.trim(); + if (startsWith(trimmedSelector, "<") && endsWith(trimmedSelector, "/>")) { + return trimmedSelector.substring(1, trimmedSelector.length - 2); + } else { + return trimmedSelector; + } + } + + function querySelectorAllExt(elt, selector) { + if (selector.indexOf("closest ") === 0) { + return [closest(elt, normalizeSelector(selector.substr(8)))]; + } else if (selector.indexOf("find ") === 0) { + return [find(elt, normalizeSelector(selector.substr(5)))]; + } else if (selector === "next") { + return [elt.nextElementSibling] + } else if (selector.indexOf("next ") === 0) { + return [scanForwardQuery(elt, normalizeSelector(selector.substr(5)))]; + } else if (selector === "previous") { + return [elt.previousElementSibling] + } else if (selector.indexOf("previous ") === 0) { + return [scanBackwardsQuery(elt, normalizeSelector(selector.substr(9)))]; + } else if (selector === 'document') { + return [document]; + } else if (selector === 'window') { + return [window]; + } else if (selector === 'body') { + return [document.body]; + } else { + return getDocument().querySelectorAll(normalizeSelector(selector)); + } + } + + var scanForwardQuery = function (start, match) { + var results = getDocument().querySelectorAll(match); + for (var i = 0; i < results.length; i++) { + var elt = results[i]; + if (elt.compareDocumentPosition(start) === Node.DOCUMENT_POSITION_PRECEDING) { + return elt; + } + } + } + + var scanBackwardsQuery = function (start, match) { + var results = getDocument().querySelectorAll(match); + for (var i = results.length - 1; i >= 0; i--) { + var elt = results[i]; + if (elt.compareDocumentPosition(start) === Node.DOCUMENT_POSITION_FOLLOWING) { + return elt; + } + } + } + + function querySelectorExt(eltOrSelector, selector) { + if (selector) { + return querySelectorAllExt(eltOrSelector, selector)[0]; + } else { + return querySelectorAllExt(getDocument().body, eltOrSelector)[0]; + } + } + + function resolveTarget(arg2) { + if (isType(arg2, 'String')) { + return find(arg2); + } else { + return arg2; + } + } + + function processEventArgs(arg1, arg2, arg3) { + if (isFunction(arg2)) { + return { + target: getDocument().body, + event: arg1, + listener: arg2 + } + } else { + return { + target: resolveTarget(arg1), + event: arg2, + listener: arg3 + } + } + + } + + function addEventListenerImpl(arg1, arg2, arg3) { + ready(function () { + var eventArgs = processEventArgs(arg1, arg2, arg3); + eventArgs.target.addEventListener(eventArgs.event, eventArgs.listener); + }) + var b = isFunction(arg2); + return b ? arg2 : arg3; + } + + function removeEventListenerImpl(arg1, arg2, arg3) { + ready(function () { + var eventArgs = processEventArgs(arg1, arg2, arg3); + eventArgs.target.removeEventListener(eventArgs.event, eventArgs.listener); + }) + return isFunction(arg2) ? arg2 : arg3; + } + + //==================================================================== + // Node processing + //==================================================================== + + var DUMMY_ELT = getDocument().createElement("output"); // dummy element for bad selectors + function findAttributeTargets(elt, attrName) { + var attrTarget = getClosestAttributeValue(elt, attrName); + if (attrTarget) { + if (attrTarget === "this") { + return [findThisElement(elt, attrName)]; + } else { + var result = querySelectorAllExt(elt, attrTarget); + if (result.length === 0) { + logError('The selector "' + attrTarget + '" on ' + attrName + " returned no matches!"); + return [DUMMY_ELT] + } else { + return result; + } + } + } + } + + function findThisElement(elt, attribute) { + return getClosestMatch(elt, function (elt) { + return getAttributeValue(elt, attribute) != null; + }) + } + + function getTarget(elt) { + var targetStr = getClosestAttributeValue(elt, "hx-target"); + if (targetStr) { + if (targetStr === "this") { + return findThisElement(elt, 'hx-target'); + } else { + return querySelectorExt(elt, targetStr) + } + } else { + var data = getInternalData(elt); + if (data.boosted) { + return getDocument().body; + } else { + return elt; + } + } + } + + function shouldSettleAttribute(name) { + var attributesToSettle = htmx.config.attributesToSettle; + for (var i = 0; i < attributesToSettle.length; i++) { + if (name === attributesToSettle[i]) { + return true; + } + } + return false; + } + + function cloneAttributes(mergeTo, mergeFrom) { + forEach(mergeTo.attributes, function (attr) { + if (!mergeFrom.hasAttribute(attr.name) && shouldSettleAttribute(attr.name)) { + mergeTo.removeAttribute(attr.name) + } + }); + forEach(mergeFrom.attributes, function (attr) { + if (shouldSettleAttribute(attr.name)) { + mergeTo.setAttribute(attr.name, attr.value); + } + }); + } + + function isInlineSwap(swapStyle, target) { + var extensions = getExtensions(target); + for (var i = 0; i < extensions.length; i++) { + var extension = extensions[i]; + try { + if (extension.isInlineSwap(swapStyle)) { + return true; + } + } catch (e) { + logError(e); + } + } + return swapStyle === "outerHTML"; + } + + /** + * + * @param {string} oobValue + * @param {HTMLElement} oobElement + * @param {*} settleInfo + * @returns + */ + function oobSwap(oobValue, oobElement, settleInfo) { + var selector = "#" + getRawAttribute(oobElement, "id"); + var swapStyle = "outerHTML"; + if (oobValue === "true") { + // do nothing + } else if (oobValue.indexOf(":") > 0) { + swapStyle = oobValue.substr(0, oobValue.indexOf(":")); + selector = oobValue.substr(oobValue.indexOf(":") + 1, oobValue.length); + } else { + swapStyle = oobValue; + } + + var targets = getDocument().querySelectorAll(selector); + if (targets) { + forEach( + targets, + function (target) { + var fragment; + var oobElementClone = oobElement.cloneNode(true); + fragment = getDocument().createDocumentFragment(); + fragment.appendChild(oobElementClone); + if (!isInlineSwap(swapStyle, target)) { + fragment = oobElementClone; // if this is not an inline swap, we use the content of the node, not the node itself + } + + var beforeSwapDetails = { shouldSwap: true, target: target, fragment: fragment }; + if (!triggerEvent(target, 'htmx:oobBeforeSwap', beforeSwapDetails)) return; + + target = beforeSwapDetails.target; // allow re-targeting + if (beforeSwapDetails['shouldSwap']) { + swap(swapStyle, target, target, fragment, settleInfo); + } + forEach(settleInfo.elts, function (elt) { + triggerEvent(elt, 'htmx:oobAfterSwap', beforeSwapDetails); + }); + } + ); + oobElement.parentNode.removeChild(oobElement); + } else { + oobElement.parentNode.removeChild(oobElement); + triggerErrorEvent(getDocument().body, "htmx:oobErrorNoTarget", { content: oobElement }); + } + return oobValue; + } + + function handleOutOfBandSwaps(elt, fragment, settleInfo) { + var oobSelects = getClosestAttributeValue(elt, "hx-select-oob"); + if (oobSelects) { + var oobSelectValues = oobSelects.split(","); + for (var i = 0; i < oobSelectValues.length; i++) { + var oobSelectValue = oobSelectValues[i].split(":", 2); + var id = oobSelectValue[0].trim(); + if (id.indexOf("#") === 0) { + id = id.substring(1); + } + var oobValue = oobSelectValue[1] || "true"; + var oobElement = fragment.querySelector("#" + id); + if (oobElement) { + oobSwap(oobValue, oobElement, settleInfo); + } + } + } + forEach(findAll(fragment, '[hx-swap-oob], [data-hx-swap-oob]'), function (oobElement) { + var oobValue = getAttributeValue(oobElement, "hx-swap-oob"); + if (oobValue != null) { + oobSwap(oobValue, oobElement, settleInfo); + } + }); + } + + function handlePreservedElements(fragment) { + forEach(findAll(fragment, '[hx-preserve], [data-hx-preserve]'), function (preservedElt) { + var id = getAttributeValue(preservedElt, "id"); + var oldElt = getDocument().getElementById(id); + if (oldElt != null) { + preservedElt.parentNode.replaceChild(oldElt, preservedElt); + } + }); + } + + function handleAttributes(parentNode, fragment, settleInfo) { + forEach(fragment.querySelectorAll("[id]"), function (newNode) { + var id = getRawAttribute(newNode, "id") + if (id && id.length > 0) { + var normalizedId = id.replace("'", "\\'"); + var normalizedTag = newNode.tagName.replace(':', '\\:'); + var oldNode = parentNode.querySelector(normalizedTag + "[id='" + normalizedId + "']"); + if (oldNode && oldNode !== parentNode) { + var newAttributes = newNode.cloneNode(); + cloneAttributes(newNode, oldNode); + settleInfo.tasks.push(function () { + cloneAttributes(newNode, newAttributes); + }); + } + } + }); + } + + function makeAjaxLoadTask(child) { + return function () { + removeClassFromElement(child, htmx.config.addedClass); + processNode(child); + processScripts(child); + processFocus(child) + triggerEvent(child, 'htmx:load'); + }; + } + + function processFocus(child) { + var autofocus = "[autofocus]"; + var autoFocusedElt = matches(child, autofocus) ? child : child.querySelector(autofocus) + if (autoFocusedElt != null) { + autoFocusedElt.focus(); + } + } + + function insertNodesBefore(parentNode, insertBefore, fragment, settleInfo) { + // DARKSTAT: handleAttributes(parentNode, fragment, settleInfo); + while (fragment.childNodes.length > 0) { + var child = fragment.firstChild; + addClassToElement(child, htmx.config.addedClass); + parentNode.insertBefore(child, insertBefore); + if (child.nodeType !== Node.TEXT_NODE && child.nodeType !== Node.COMMENT_NODE) { + settleInfo.tasks.push(makeAjaxLoadTask(child)); + } + } + } + + // based on https://gist.github.com/hyamamoto/fd435505d29ebfa3d9716fd2be8d42f0, + // derived from Java's string hashcode implementation + function stringHash(string, hash) { + var char = 0; + while (char < string.length) { + hash = (hash << 5) - hash + string.charCodeAt(char++) | 0; // bitwise or ensures we have a 32-bit int + } + return hash; + } + + function attributeHash(elt) { + var hash = 0; + // IE fix + if (elt.attributes) { + for (var i = 0; i < elt.attributes.length; i++) { + var attribute = elt.attributes[i]; + if (attribute.value) { // only include attributes w/ actual values (empty is same as non-existent) + hash = stringHash(attribute.name, hash); + hash = stringHash(attribute.value, hash); + } + } + } + return hash; + } + + function deInitOnHandlers(elt) { + var internalData = getInternalData(elt); + if (internalData.onHandlers) { + for (var i = 0; i < internalData.onHandlers.length; i++) { + const handlerInfo = internalData.onHandlers[i]; + elt.removeEventListener(handlerInfo.event, handlerInfo.listener); + } + delete internalData.onHandlers + } + } + + function deInitNode(element) { + var internalData = getInternalData(element); + if (internalData.timeout) { + clearTimeout(internalData.timeout); + } + if (internalData.webSocket) { + internalData.webSocket.close(); + } + if (internalData.sseEventSource) { + internalData.sseEventSource.close(); + } + if (internalData.listenerInfos) { + forEach(internalData.listenerInfos, function (info) { + if (info.on) { + info.on.removeEventListener(info.trigger, info.listener); + } + }); + } + deInitOnHandlers(element); + forEach(Object.keys(internalData), function (key) { delete internalData[key] }); + } + + function cleanUpElement(element) { + triggerEvent(element, "htmx:beforeCleanupElement") + deInitNode(element); + // if (element.children) { // IE + // forEach(element.children, function(child) { cleanUpElement(child) }); + // } + } + + function swapOuterHTML(target, fragment, settleInfo) { + if (target.tagName === "BODY") { + return swapInnerHTML(target, fragment, settleInfo); + } else { + // @type {HTMLElement} + var newElt + var eltBeforeNewContent = target.previousSibling; + insertNodesBefore(parentElt(target), target, fragment, settleInfo); + if (eltBeforeNewContent == null) { + newElt = parentElt(target).firstChild; + } else { + newElt = eltBeforeNewContent.nextSibling; + } + settleInfo.elts = settleInfo.elts.filter(function (e) { return e != target }); + while (newElt && newElt !== target) { + if (newElt.nodeType === Node.ELEMENT_NODE) { + settleInfo.elts.push(newElt); + } + newElt = newElt.nextElementSibling; + } + cleanUpElement(target); + parentElt(target).removeChild(target); + } + } + + function swapAfterBegin(target, fragment, settleInfo) { + return insertNodesBefore(target, target.firstChild, fragment, settleInfo); + } + + function swapBeforeBegin(target, fragment, settleInfo) { + return insertNodesBefore(parentElt(target), target, fragment, settleInfo); + } + + function swapBeforeEnd(target, fragment, settleInfo) { + return insertNodesBefore(target, null, fragment, settleInfo); + } + + function swapAfterEnd(target, fragment, settleInfo) { + return insertNodesBefore(parentElt(target), target.nextSibling, fragment, settleInfo); + } + function swapDelete(target, fragment, settleInfo) { + cleanUpElement(target); + return parentElt(target).removeChild(target); + } + + function swapInnerHTML(target, fragment, settleInfo) { + var firstChild = target.firstChild; + insertNodesBefore(target, firstChild, fragment, settleInfo); + if (firstChild) { + while (firstChild.nextSibling) { + cleanUpElement(firstChild.nextSibling) + target.removeChild(firstChild.nextSibling); + } + cleanUpElement(firstChild) + target.removeChild(firstChild); + } + } + + function maybeSelectFromResponse(elt, fragment, selectOverride) { + var selector = selectOverride || getClosestAttributeValue(elt, "hx-select"); + if (selector) { + var newFragment = getDocument().createDocumentFragment(); + forEach(fragment.querySelectorAll(selector), function (node) { + newFragment.appendChild(node); + }); + fragment = newFragment; + } + return fragment; + } + + function swap(swapStyle, elt, target, fragment, settleInfo) { + switch (swapStyle) { + case "none": + return; + case "outerHTML": + swapOuterHTML(target, fragment, settleInfo); + return; + case "afterbegin": + swapAfterBegin(target, fragment, settleInfo); + return; + case "beforebegin": + swapBeforeBegin(target, fragment, settleInfo); + return; + case "beforeend": + swapBeforeEnd(target, fragment, settleInfo); + return; + case "afterend": + swapAfterEnd(target, fragment, settleInfo); + return; + case "delete": + swapDelete(target, fragment, settleInfo); + return; + default: + var extensions = getExtensions(elt); + for (var i = 0; i < extensions.length; i++) { + var ext = extensions[i]; + try { + var newElements = ext.handleSwap(swapStyle, target, fragment, settleInfo); + if (newElements) { + if (typeof newElements.length !== 'undefined') { + // if handleSwap returns an array (like) of elements, we handle them + for (var j = 0; j < newElements.length; j++) { + var child = newElements[j]; + if (child.nodeType !== Node.TEXT_NODE && child.nodeType !== Node.COMMENT_NODE) { + settleInfo.tasks.push(makeAjaxLoadTask(child)); + } + } + } + return; + } + } catch (e) { + logError(e); + } + } + if (swapStyle === "innerHTML") { + swapInnerHTML(target, fragment, settleInfo); + } else { + swap(htmx.config.defaultSwapStyle, elt, target, fragment, settleInfo); + } + } + } + + function findTitle(content) { + if (content.indexOf(' -1) { + var contentWithSvgsRemoved = content.replace(SVG_TAGS_REGEX, ''); + var result = contentWithSvgsRemoved.match(TITLE_TAG_REGEX); + if (result) { + return result[2]; + } + } + } + + function selectAndSwap(swapStyle, target, elt, responseText, settleInfo, selectOverride) { + settleInfo.title = findTitle(responseText); + var fragment = makeFragment(responseText); + if (fragment) { + handleOutOfBandSwaps(elt, fragment, settleInfo); + fragment = maybeSelectFromResponse(elt, fragment, selectOverride); + handlePreservedElements(fragment); + return swap(swapStyle, elt, target, fragment, settleInfo); + } + } + + function handleTrigger(xhr, header, elt) { + var triggerBody = xhr.getResponseHeader(header); + if (triggerBody.indexOf("{") === 0) { + var triggers = parseJSON(triggerBody); + for (var eventName in triggers) { + if (triggers.hasOwnProperty(eventName)) { + var detail = triggers[eventName]; + if (!isRawObject(detail)) { + detail = { "value": detail } + } + triggerEvent(elt, eventName, detail); + } + } + } else { + var eventNames = triggerBody.split(",") + for (var i = 0; i < eventNames.length; i++) { + triggerEvent(elt, eventNames[i].trim(), []); + } + } + } + + var WHITESPACE = /\s/; + var WHITESPACE_OR_COMMA = /[\s,]/; + var SYMBOL_START = /[_$a-zA-Z]/; + var SYMBOL_CONT = /[_$a-zA-Z0-9]/; + var STRINGISH_START = ['"', "'", "/"]; + var NOT_WHITESPACE = /[^\s]/; + var COMBINED_SELECTOR_START = /[{(]/; + var COMBINED_SELECTOR_END = /[})]/; + function tokenizeString(str) { + var tokens = []; + var position = 0; + while (position < str.length) { + if (SYMBOL_START.exec(str.charAt(position))) { + var startPosition = position; + while (SYMBOL_CONT.exec(str.charAt(position + 1))) { + position++; + } + tokens.push(str.substr(startPosition, position - startPosition + 1)); + } else if (STRINGISH_START.indexOf(str.charAt(position)) !== -1) { + var startChar = str.charAt(position); + var startPosition = position; + position++; + while (position < str.length && str.charAt(position) !== startChar) { + if (str.charAt(position) === "\\") { + position++; + } + position++; + } + tokens.push(str.substr(startPosition, position - startPosition + 1)); + } else { + var symbol = str.charAt(position); + tokens.push(symbol); + } + position++; + } + return tokens; + } + + function isPossibleRelativeReference(token, last, paramName) { + return SYMBOL_START.exec(token.charAt(0)) && + token !== "true" && + token !== "false" && + token !== "this" && + token !== paramName && + last !== "."; + } + + function maybeGenerateConditional(elt, tokens, paramName) { + if (tokens[0] === '[') { + tokens.shift(); + var bracketCount = 1; + var conditionalSource = " return (function(" + paramName + "){ return ("; + var last = null; + while (tokens.length > 0) { + var token = tokens[0]; + if (token === "]") { + bracketCount--; + if (bracketCount === 0) { + if (last === null) { + conditionalSource = conditionalSource + "true"; + } + tokens.shift(); + conditionalSource += ")})"; + try { + var conditionFunction = maybeEval(elt, function () { + return Function(conditionalSource)(); + }, + function () { return true }) + conditionFunction.source = conditionalSource; + return conditionFunction; + } catch (e) { + triggerErrorEvent(getDocument().body, "htmx:syntax:error", { error: e, source: conditionalSource }) + return null; + } + } + } else if (token === "[") { + bracketCount++; + } + if (isPossibleRelativeReference(token, last, paramName)) { + conditionalSource += "((" + paramName + "." + token + ") ? (" + paramName + "." + token + ") : (window." + token + "))"; + } else { + conditionalSource = conditionalSource + token; + } + last = tokens.shift(); + } + } + } + + function consumeUntil(tokens, match) { + var result = ""; + while (tokens.length > 0 && !match.test(tokens[0])) { + result += tokens.shift(); + } + return result; + } + + function consumeCSSSelector(tokens) { + var result; + if (tokens.length > 0 && COMBINED_SELECTOR_START.test(tokens[0])) { + tokens.shift(); + result = consumeUntil(tokens, COMBINED_SELECTOR_END).trim(); + tokens.shift(); + } else { + result = consumeUntil(tokens, WHITESPACE_OR_COMMA); + } + return result; + } + + var INPUT_SELECTOR = 'input, textarea, select'; + + /** + * @param {HTMLElement} elt + * @param {string} explicitTrigger + * @param {cache} cache for trigger specs + * @returns {import("./htmx").HtmxTriggerSpecification[]} + */ + function parseAndCacheTrigger(elt, explicitTrigger, cache) { + var triggerSpecs = []; + var tokens = tokenizeString(explicitTrigger); + do { + consumeUntil(tokens, NOT_WHITESPACE); + var initialLength = tokens.length; + var trigger = consumeUntil(tokens, /[,\[\s]/); + if (trigger !== "") { + if (trigger === "every") { + var every = { trigger: 'every' }; + consumeUntil(tokens, NOT_WHITESPACE); + every.pollInterval = parseInterval(consumeUntil(tokens, /[,\[\s]/)); + consumeUntil(tokens, NOT_WHITESPACE); + var eventFilter = maybeGenerateConditional(elt, tokens, "event"); + if (eventFilter) { + every.eventFilter = eventFilter; + } + triggerSpecs.push(every); + } else if (trigger.indexOf("sse:") === 0) { + triggerSpecs.push({ trigger: 'sse', sseEvent: trigger.substr(4) }); + } else { + var triggerSpec = { trigger: trigger }; + var eventFilter = maybeGenerateConditional(elt, tokens, "event"); + if (eventFilter) { + triggerSpec.eventFilter = eventFilter; + } + while (tokens.length > 0 && tokens[0] !== ",") { + consumeUntil(tokens, NOT_WHITESPACE) + var token = tokens.shift(); + if (token === "changed") { + triggerSpec.changed = true; + } else if (token === "once") { + triggerSpec.once = true; + } else if (token === "consume") { + triggerSpec.consume = true; + } else if (token === "delay" && tokens[0] === ":") { + tokens.shift(); + triggerSpec.delay = parseInterval(consumeUntil(tokens, WHITESPACE_OR_COMMA)); + } else if (token === "from" && tokens[0] === ":") { + tokens.shift(); + if (COMBINED_SELECTOR_START.test(tokens[0])) { + var from_arg = consumeCSSSelector(tokens); + } else { + var from_arg = consumeUntil(tokens, WHITESPACE_OR_COMMA); + if (from_arg === "closest" || from_arg === "find" || from_arg === "next" || from_arg === "previous") { + tokens.shift(); + var selector = consumeCSSSelector(tokens); + // `next` and `previous` allow a selector-less syntax + if (selector.length > 0) { + from_arg += " " + selector; + } + } + } + triggerSpec.from = from_arg; + } else if (token === "target" && tokens[0] === ":") { + tokens.shift(); + triggerSpec.target = consumeCSSSelector(tokens); + } else if (token === "throttle" && tokens[0] === ":") { + tokens.shift(); + triggerSpec.throttle = parseInterval(consumeUntil(tokens, WHITESPACE_OR_COMMA)); + } else if (token === "queue" && tokens[0] === ":") { + tokens.shift(); + triggerSpec.queue = consumeUntil(tokens, WHITESPACE_OR_COMMA); + } else if (token === "root" && tokens[0] === ":") { + tokens.shift(); + triggerSpec[token] = consumeCSSSelector(tokens); + } else if (token === "threshold" && tokens[0] === ":") { + tokens.shift(); + triggerSpec[token] = consumeUntil(tokens, WHITESPACE_OR_COMMA); + } else { + triggerErrorEvent(elt, "htmx:syntax:error", { token: tokens.shift() }); + } + } + triggerSpecs.push(triggerSpec); + } + } + if (tokens.length === initialLength) { + triggerErrorEvent(elt, "htmx:syntax:error", { token: tokens.shift() }); + } + consumeUntil(tokens, NOT_WHITESPACE); + } while (tokens[0] === "," && tokens.shift()) + if (cache) { + cache[explicitTrigger] = triggerSpecs + } + return triggerSpecs + } + + /** + * @param {HTMLElement} elt + * @returns {import("./htmx").HtmxTriggerSpecification[]} + */ + function getTriggerSpecs(elt) { + var explicitTrigger = getAttributeValue(elt, 'hx-trigger'); + var triggerSpecs = []; + if (explicitTrigger) { + var cache = htmx.config.triggerSpecsCache + triggerSpecs = (cache && cache[explicitTrigger]) || parseAndCacheTrigger(elt, explicitTrigger, cache) + } + + if (triggerSpecs.length > 0) { + return triggerSpecs; + } else if (matches(elt, 'form')) { + return [{ trigger: 'submit' }]; + } else if (matches(elt, 'input[type="button"], input[type="submit"]')) { + return [{ trigger: 'click' }]; + } else if (matches(elt, INPUT_SELECTOR)) { + return [{ trigger: 'change' }]; + } else { + return [{ trigger: 'click' }]; + } + } + + function cancelPolling(elt) { + getInternalData(elt).cancelled = true; + } + + function processPolling(elt, handler, spec) { + var nodeData = getInternalData(elt); + nodeData.timeout = setTimeout(function () { + if (bodyContains(elt) && nodeData.cancelled !== true) { + if (!maybeFilterEvent(spec, elt, makeEvent('hx:poll:trigger', { + triggerSpec: spec, + target: elt + }))) { + handler(elt); + } + processPolling(elt, handler, spec); + } + }, spec.pollInterval); + } + + function isLocalLink(elt) { + return location.hostname === elt.hostname && + getRawAttribute(elt, 'href') && + getRawAttribute(elt, 'href').indexOf("#") !== 0; + } + + function boostElement(elt, nodeData, triggerSpecs) { + if ((elt.tagName === "A" && isLocalLink(elt) && (elt.target === "" || elt.target === "_self")) || elt.tagName === "FORM") { + nodeData.boosted = true; + var verb, path; + if (elt.tagName === "A") { + verb = "get"; + path = getRawAttribute(elt, 'href') + } else { + var rawAttribute = getRawAttribute(elt, "method"); + verb = rawAttribute ? rawAttribute.toLowerCase() : "get"; + if (verb === "get") { + } + path = getRawAttribute(elt, 'action'); + } + triggerSpecs.forEach(function (triggerSpec) { + addEventListener(elt, function (elt, evt) { + if (closest(elt, htmx.config.disableSelector)) { + cleanUpElement(elt) + return + } + issueAjaxRequest(verb, path, elt, evt) + }, nodeData, triggerSpec, true); + }); + } + } + + /** + * + * @param {Event} evt + * @param {HTMLElement} elt + * @returns + */ + function shouldCancel(evt, elt) { + if (evt.type === "submit" || evt.type === "click") { + if (elt.tagName === "FORM") { + return true; + } + if (matches(elt, 'input[type="submit"], button') && closest(elt, 'form') !== null) { + return true; + } + if (elt.tagName === "A" && elt.href && + (elt.getAttribute('href') === '#' || elt.getAttribute('href').indexOf("#") !== 0)) { + return true; + } + } + return false; + } + + function ignoreBoostedAnchorCtrlClick(elt, evt) { + return getInternalData(elt).boosted && elt.tagName === "A" && evt.type === "click" && (evt.ctrlKey || evt.metaKey); + } + + function maybeFilterEvent(triggerSpec, elt, evt) { + var eventFilter = triggerSpec.eventFilter; + if (eventFilter) { + try { + return eventFilter.call(elt, evt) !== true; + } catch (e) { + triggerErrorEvent(getDocument().body, "htmx:eventFilter:error", { error: e, source: eventFilter.source }); + return true; + } + } + return false; + } + + function addEventListener(elt, handler, nodeData, triggerSpec, explicitCancel) { + var elementData = getInternalData(elt); + var eltsToListenOn; + if (triggerSpec.from) { + eltsToListenOn = querySelectorAllExt(elt, triggerSpec.from); + } else { + eltsToListenOn = [elt]; + } + // store the initial values of the elements, so we can tell if they change + if (triggerSpec.changed) { + eltsToListenOn.forEach(function (eltToListenOn) { + var eltToListenOnData = getInternalData(eltToListenOn); + eltToListenOnData.lastValue = eltToListenOn.value; + }) + } + forEach(eltsToListenOn, function (eltToListenOn) { + var eventListener = function (evt) { + if (!bodyContains(elt)) { + eltToListenOn.removeEventListener(triggerSpec.trigger, eventListener); + return; + } + if (ignoreBoostedAnchorCtrlClick(elt, evt)) { + return; + } + if (explicitCancel || shouldCancel(evt, elt)) { + evt.preventDefault(); + } + if (maybeFilterEvent(triggerSpec, elt, evt)) { + return; + } + var eventData = getInternalData(evt); + eventData.triggerSpec = triggerSpec; + if (eventData.handledFor == null) { + eventData.handledFor = []; + } + if (eventData.handledFor.indexOf(elt) < 0) { + eventData.handledFor.push(elt); + if (triggerSpec.consume) { + evt.stopPropagation(); + } + if (triggerSpec.target && evt.target) { + if (!matches(evt.target, triggerSpec.target)) { + return; + } + } + if (triggerSpec.once) { + if (elementData.triggeredOnce) { + return; + } else { + elementData.triggeredOnce = true; + } + } + if (triggerSpec.changed) { + var eltToListenOnData = getInternalData(eltToListenOn) + if (eltToListenOnData.lastValue === eltToListenOn.value) { + return; + } + eltToListenOnData.lastValue = eltToListenOn.value + } + if (elementData.delayed) { + clearTimeout(elementData.delayed); + } + if (elementData.throttle) { + return; + } + + if (triggerSpec.throttle > 0) { + if (!elementData.throttle) { + handler(elt, evt); + elementData.throttle = setTimeout(function () { + elementData.throttle = null; + }, triggerSpec.throttle); + } + } else if (triggerSpec.delay > 0) { + elementData.delayed = setTimeout(function () { handler(elt, evt) }, triggerSpec.delay); + } else { + triggerEvent(elt, 'htmx:trigger') + handler(elt, evt); + } + } + }; + if (nodeData.listenerInfos == null) { + nodeData.listenerInfos = []; + } + nodeData.listenerInfos.push({ + trigger: triggerSpec.trigger, + listener: eventListener, + on: eltToListenOn + }) + eltToListenOn.addEventListener(triggerSpec.trigger, eventListener); + }); + } + + var windowIsScrolling = false // used by initScrollHandler + var scrollHandler = null; + function initScrollHandler() { + if (!scrollHandler) { + scrollHandler = function () { + windowIsScrolling = true + }; + window.addEventListener("scroll", scrollHandler) + setInterval(function () { + if (windowIsScrolling) { + windowIsScrolling = false; + forEach(getDocument().querySelectorAll("[hx-trigger='revealed'],[data-hx-trigger='revealed']"), function (elt) { + maybeReveal(elt); + }) + } + }, 200); + } + } + + function maybeReveal(elt) { + if (!hasAttribute(elt, 'data-hx-revealed') && isScrolledIntoView(elt)) { + elt.setAttribute('data-hx-revealed', 'true'); + var nodeData = getInternalData(elt); + if (nodeData.initHash) { + triggerEvent(elt, 'revealed'); + } else { + // if the node isn't initialized, wait for it before triggering the request + elt.addEventListener("htmx:afterProcessNode", function (evt) { triggerEvent(elt, 'revealed') }, { once: true }); + } + } + } + + //==================================================================== + // Web Sockets + //==================================================================== + + function processWebSocketInfo(elt, nodeData, info) { + var values = splitOnWhitespace(info); + for (var i = 0; i < values.length; i++) { + var value = values[i].split(/:(.+)/); + if (value[0] === "connect") { + ensureWebSocket(elt, value[1], 0); + } + if (value[0] === "send") { + processWebSocketSend(elt); + } + } + } + + function ensureWebSocket(elt, wssSource, retryCount) { + if (!bodyContains(elt)) { + return; // stop ensuring websocket connection when socket bearing element ceases to exist + } + + if (wssSource.indexOf("/") == 0) { // complete absolute paths only + var base_part = location.hostname + (location.port ? ':' + location.port : ''); + if (location.protocol == 'https:') { + wssSource = "wss://" + base_part + wssSource; + } else if (location.protocol == 'http:') { + wssSource = "ws://" + base_part + wssSource; + } + } + var socket = htmx.createWebSocket(wssSource); + socket.onerror = function (e) { + triggerErrorEvent(elt, "htmx:wsError", { error: e, socket: socket }); + maybeCloseWebSocketSource(elt); + }; + + socket.onclose = function (e) { + if ([1006, 1012, 1013].indexOf(e.code) >= 0) { // Abnormal Closure/Service Restart/Try Again Later + var delay = getWebSocketReconnectDelay(retryCount); + setTimeout(function () { + ensureWebSocket(elt, wssSource, retryCount + 1); // creates a websocket with a new timeout + }, delay); + } + }; + socket.onopen = function (e) { + retryCount = 0; + } + + getInternalData(elt).webSocket = socket; + socket.addEventListener('message', function (event) { + if (maybeCloseWebSocketSource(elt)) { + return; + } + + var response = event.data; + withExtensions(elt, function (extension) { + response = extension.transformResponse(response, null, elt); + }); + + var settleInfo = makeSettleInfo(elt); + var fragment = makeFragment(response); + var children = toArray(fragment.children); + for (var i = 0; i < children.length; i++) { + var child = children[i]; + oobSwap(getAttributeValue(child, "hx-swap-oob") || "true", child, settleInfo); + } + + settleImmediately(settleInfo.tasks); + }); + } + + function maybeCloseWebSocketSource(elt) { + if (!bodyContains(elt)) { + getInternalData(elt).webSocket.close(); + return true; + } + } + + function processWebSocketSend(elt) { + var webSocketSourceElt = getClosestMatch(elt, function (parent) { + return getInternalData(parent).webSocket != null; + }); + if (webSocketSourceElt) { + elt.addEventListener(getTriggerSpecs(elt)[0].trigger, function (evt) { + var webSocket = getInternalData(webSocketSourceElt).webSocket; + var headers = getHeaders(elt, webSocketSourceElt); + var results = getInputValues(elt, 'post'); + var errors = results.errors; + var rawParameters = results.values; + var expressionVars = getExpressionVars(elt); + var allParameters = mergeObjects(rawParameters, expressionVars); + var filteredParameters = filterValues(allParameters, elt); + filteredParameters['HEADERS'] = headers; + if (errors && errors.length > 0) { + triggerEvent(elt, 'htmx:validation:halted', errors); + return; + } + webSocket.send(JSON.stringify(filteredParameters)); + if (shouldCancel(evt, elt)) { + evt.preventDefault(); + } + }); + } else { + triggerErrorEvent(elt, "htmx:noWebSocketSourceError"); + } + } + + function getWebSocketReconnectDelay(retryCount) { + var delay = htmx.config.wsReconnectDelay; + if (typeof delay === 'function') { + // @ts-ignore + return delay(retryCount); + } + if (delay === 'full-jitter') { + var exp = Math.min(retryCount, 6); + var maxDelay = 1000 * Math.pow(2, exp); + return maxDelay * Math.random(); + } + logError('htmx.config.wsReconnectDelay must either be a function or the string "full-jitter"'); + } + + //==================================================================== + // Server Sent Events + //==================================================================== + + function processSSEInfo(elt, nodeData, info) { + var values = splitOnWhitespace(info); + for (var i = 0; i < values.length; i++) { + var value = values[i].split(/:(.+)/); + if (value[0] === "connect") { + processSSESource(elt, value[1]); + } + + if ((value[0] === "swap")) { + processSSESwap(elt, value[1]) + } + } + } + + function processSSESource(elt, sseSrc) { + var source = htmx.createEventSource(sseSrc); + source.onerror = function (e) { + triggerErrorEvent(elt, "htmx:sseError", { error: e, source: source }); + maybeCloseSSESource(elt); + }; + getInternalData(elt).sseEventSource = source; + } + + function processSSESwap(elt, sseEventName) { + var sseSourceElt = getClosestMatch(elt, hasEventSource); + if (sseSourceElt) { + var sseEventSource = getInternalData(sseSourceElt).sseEventSource; + var sseListener = function (event) { + if (maybeCloseSSESource(sseSourceElt)) { + return; + } + if (!bodyContains(elt)) { + sseEventSource.removeEventListener(sseEventName, sseListener); + return; + } + + /////////////////////////// + // TODO: merge this code with AJAX and WebSockets code in the future. + + var response = event.data; + withExtensions(elt, function (extension) { + response = extension.transformResponse(response, null, elt); + }); + + var swapSpec = getSwapSpecification(elt) + var target = getTarget(elt) + var settleInfo = makeSettleInfo(elt); + + selectAndSwap(swapSpec.swapStyle, target, elt, response, settleInfo) + settleImmediately(settleInfo.tasks) + triggerEvent(elt, "htmx:sseMessage", event) + }; + + getInternalData(elt).sseListener = sseListener; + sseEventSource.addEventListener(sseEventName, sseListener); + } else { + triggerErrorEvent(elt, "htmx:noSSESourceError"); + } + } + + function processSSETrigger(elt, handler, sseEventName) { + var sseSourceElt = getClosestMatch(elt, hasEventSource); + if (sseSourceElt) { + var sseEventSource = getInternalData(sseSourceElt).sseEventSource; + var sseListener = function () { + if (!maybeCloseSSESource(sseSourceElt)) { + if (bodyContains(elt)) { + handler(elt); + } else { + sseEventSource.removeEventListener(sseEventName, sseListener); + } + } + }; + getInternalData(elt).sseListener = sseListener; + sseEventSource.addEventListener(sseEventName, sseListener); + } else { + triggerErrorEvent(elt, "htmx:noSSESourceError"); + } + } + + function maybeCloseSSESource(elt) { + if (!bodyContains(elt)) { + getInternalData(elt).sseEventSource.close(); + return true; + } + } + + function hasEventSource(node) { + return getInternalData(node).sseEventSource != null; + } + + //==================================================================== + + function loadImmediately(elt, handler, nodeData, delay) { + var load = function () { + if (!nodeData.loaded) { + nodeData.loaded = true; + handler(elt); + } + } + if (delay > 0) { + setTimeout(load, delay); + } else { + load(); + } + } + + function processVerbs(elt, nodeData, triggerSpecs) { + var explicitAction = false; + forEach(VERBS, function (verb) { + if (hasAttribute(elt, 'hx-' + verb)) { + var path = getAttributeValue(elt, 'hx-' + verb); + explicitAction = true; + nodeData.path = path; + nodeData.verb = verb; + triggerSpecs.forEach(function (triggerSpec) { + addTriggerHandler(elt, triggerSpec, nodeData, function (elt, evt) { + if (closest(elt, htmx.config.disableSelector)) { + cleanUpElement(elt) + return + } + issueAjaxRequest(verb, path, elt, evt) + }) + }); + } + }); + return explicitAction; + } + + function addTriggerHandler(elt, triggerSpec, nodeData, handler) { + if (triggerSpec.sseEvent) { + processSSETrigger(elt, handler, triggerSpec.sseEvent); + } else if (triggerSpec.trigger === "revealed") { + initScrollHandler(); + addEventListener(elt, handler, nodeData, triggerSpec); + maybeReveal(elt); + } else if (triggerSpec.trigger === "intersect") { + var observerOptions = {}; + if (triggerSpec.root) { + observerOptions.root = querySelectorExt(elt, triggerSpec.root) + } + if (triggerSpec.threshold) { + observerOptions.threshold = parseFloat(triggerSpec.threshold); + } + var observer = new IntersectionObserver(function (entries) { + for (var i = 0; i < entries.length; i++) { + var entry = entries[i]; + if (entry.isIntersecting) { + triggerEvent(elt, "intersect"); + break; + } + } + }, observerOptions); + observer.observe(elt); + addEventListener(elt, handler, nodeData, triggerSpec); + } else if (triggerSpec.trigger === "load") { + if (!maybeFilterEvent(triggerSpec, elt, makeEvent("load", { elt: elt }))) { + loadImmediately(elt, handler, nodeData, triggerSpec.delay); + } + } else if (triggerSpec.pollInterval > 0) { + nodeData.polling = true; + processPolling(elt, handler, triggerSpec); + } else { + addEventListener(elt, handler, nodeData, triggerSpec); + } + } + + function evalScript(script) { + if (!script.htmxExecuted && htmx.config.allowScriptTags && + (script.type === "text/javascript" || script.type === "module" || script.type === "")) { + var newScript = getDocument().createElement("script"); + forEach(script.attributes, function (attr) { + newScript.setAttribute(attr.name, attr.value); + }); + newScript.textContent = script.textContent; + newScript.async = false; + if (htmx.config.inlineScriptNonce) { + newScript.nonce = htmx.config.inlineScriptNonce; + } + var parent = script.parentElement; + + try { + parent.insertBefore(newScript, script); + } catch (e) { + logError(e); + } finally { + // remove old script element, but only if it is still in DOM + if (script.parentElement) { + script.parentElement.removeChild(script); + } + } + } + } + + function processScripts(elt) { + if (matches(elt, "script")) { + evalScript(elt); + } + forEach(findAll(elt, "script"), function (script) { + evalScript(script); + }); + } + + function shouldProcessHxOn(elt) { + var attributes = elt.attributes + for (var j = 0; j < attributes.length; j++) { + var attrName = attributes[j].name + if (startsWith(attrName, "hx-on:") || startsWith(attrName, "data-hx-on:") || + startsWith(attrName, "hx-on-") || startsWith(attrName, "data-hx-on-")) { + return true + } + } + return false + } + + function findHxOnWildcardElements(elt) { + var node = null + var elements = [] + + if (shouldProcessHxOn(elt)) { + elements.push(elt) + } + + if (document.evaluate) { + var iter = document.evaluate('.//*[@*[ starts-with(name(), "hx-on:") or starts-with(name(), "data-hx-on:") or' + + ' starts-with(name(), "hx-on-") or starts-with(name(), "data-hx-on-") ]]', elt) + while (node = iter.iterateNext()) elements.push(node) + } else { + var allElements = elt.getElementsByTagName("*") + for (var i = 0; i < allElements.length; i++) { + if (shouldProcessHxOn(allElements[i])) { + elements.push(allElements[i]) + } + } + } + + return elements + } + + function findElementsToProcess(elt) { + if (elt.querySelectorAll) { + var boostedSelector = ", [hx-boost] a, [data-hx-boost] a, a[hx-boost], a[data-hx-boost]"; + var results = elt.querySelectorAll(VERB_SELECTOR + boostedSelector + ", form, [type='submit'], [hx-sse], [data-hx-sse], [hx-ws]," + + " [data-hx-ws], [hx-ext], [data-hx-ext], [hx-trigger], [data-hx-trigger], [hx-on], [data-hx-on]"); + return results; + } else { + return []; + } + } + + // Handle submit buttons/inputs that have the form attribute set + // see https://developer.mozilla.org/docs/Web/HTML/Element/button + function maybeSetLastButtonClicked(evt) { + var elt = closest(evt.target, "button, input[type='submit']"); + var internalData = getRelatedFormData(evt) + if (internalData) { + internalData.lastButtonClicked = elt; + } + }; + function maybeUnsetLastButtonClicked(evt) { + var internalData = getRelatedFormData(evt) + if (internalData) { + internalData.lastButtonClicked = null; + } + } + function getRelatedFormData(evt) { + var elt = closest(evt.target, "button, input[type='submit']"); + if (!elt) { + return; + } + var form = resolveTarget('#' + getRawAttribute(elt, 'form')) || closest(elt, 'form'); + if (!form) { + return; + } + return getInternalData(form); + } + function initButtonTracking(elt) { + // need to handle both click and focus in: + // focusin - in case someone tabs in to a button and hits the space bar + // click - on OSX buttons do not focus on click see https://bugs.webkit.org/show_bug.cgi?id=13724 + elt.addEventListener('click', maybeSetLastButtonClicked) + elt.addEventListener('focusin', maybeSetLastButtonClicked) + elt.addEventListener('focusout', maybeUnsetLastButtonClicked) + } + + function countCurlies(line) { + var tokens = tokenizeString(line); + var netCurlies = 0; + for (var i = 0; i < tokens.length; i++) { + const token = tokens[i]; + if (token === "{") { + netCurlies++; + } else if (token === "}") { + netCurlies--; + } + } + return netCurlies; + } + + function addHxOnEventHandler(elt, eventName, code) { + var nodeData = getInternalData(elt); + if (!Array.isArray(nodeData.onHandlers)) { + nodeData.onHandlers = []; + } + var func; + var listener = function (e) { + return maybeEval(elt, function () { + if (!func) { + func = new Function("event", code); + } + func.call(elt, e); + }); + }; + elt.addEventListener(eventName, listener); + nodeData.onHandlers.push({ event: eventName, listener: listener }); + } + + function processHxOn(elt) { + var hxOnValue = getAttributeValue(elt, 'hx-on'); + if (hxOnValue) { + var handlers = {} + var lines = hxOnValue.split("\n"); + var currentEvent = null; + var curlyCount = 0; + while (lines.length > 0) { + var line = lines.shift(); + var match = line.match(/^\s*([a-zA-Z:\-\.]+:)(.*)/); + if (curlyCount === 0 && match) { + line.split(":") + currentEvent = match[1].slice(0, -1); // strip last colon + handlers[currentEvent] = match[2]; + } else { + handlers[currentEvent] += line; + } + curlyCount += countCurlies(line); + } + + for (var eventName in handlers) { + addHxOnEventHandler(elt, eventName, handlers[eventName]); + } + } + } + + function processHxOnWildcard(elt) { + // wipe any previous on handlers so that this function takes precedence + deInitOnHandlers(elt) + + for (var i = 0; i < elt.attributes.length; i++) { + var name = elt.attributes[i].name + var value = elt.attributes[i].value + if (startsWith(name, "hx-on") || startsWith(name, "data-hx-on")) { + var afterOnPosition = name.indexOf("-on") + 3; + var nextChar = name.slice(afterOnPosition, afterOnPosition + 1); + if (nextChar === "-" || nextChar === ":") { + var eventName = name.slice(afterOnPosition + 1); + // if the eventName starts with a colon or dash, prepend "htmx" for shorthand support + if (startsWith(eventName, ":")) { + eventName = "htmx" + eventName + } else if (startsWith(eventName, "-")) { + eventName = "htmx:" + eventName.slice(1); + } else if (startsWith(eventName, "htmx-")) { + eventName = "htmx:" + eventName.slice(5); + } + + addHxOnEventHandler(elt, eventName, value) + } + } + } + } + + function initNode(elt) { + if (closest(elt, htmx.config.disableSelector)) { + cleanUpElement(elt) + return; + } + var nodeData = getInternalData(elt); + if (nodeData.initHash !== attributeHash(elt)) { + // clean up any previously processed info + deInitNode(elt); + + nodeData.initHash = attributeHash(elt); + + processHxOn(elt); + + triggerEvent(elt, "htmx:beforeProcessNode") + + if (elt.value) { + nodeData.lastValue = elt.value; + } + + var triggerSpecs = getTriggerSpecs(elt); + var hasExplicitHttpAction = processVerbs(elt, nodeData, triggerSpecs); + + if (!hasExplicitHttpAction) { + if (getClosestAttributeValue(elt, "hx-boost") === "true") { + boostElement(elt, nodeData, triggerSpecs); + } else if (hasAttribute(elt, 'hx-trigger')) { + triggerSpecs.forEach(function (triggerSpec) { + // For "naked" triggers, don't do anything at all + addTriggerHandler(elt, triggerSpec, nodeData, function () { + }) + }) + } + } + + // Handle submit buttons/inputs that have the form attribute set + // see https://developer.mozilla.org/docs/Web/HTML/Element/button + if (elt.tagName === "FORM" || (getRawAttribute(elt, "type") === "submit" && hasAttribute(elt, "form"))) { + initButtonTracking(elt) + } + + var sseInfo = getAttributeValue(elt, 'hx-sse'); + if (sseInfo) { + processSSEInfo(elt, nodeData, sseInfo); + } + + var wsInfo = getAttributeValue(elt, 'hx-ws'); + if (wsInfo) { + processWebSocketInfo(elt, nodeData, wsInfo); + } + triggerEvent(elt, "htmx:afterProcessNode"); + } + } + + function processNode(elt) { + elt = resolveTarget(elt); + if (closest(elt, htmx.config.disableSelector)) { + cleanUpElement(elt) + return; + } + initNode(elt); + forEach(findElementsToProcess(elt), function (child) { initNode(child) }); + // Because it happens second, the new way of adding onHandlers superseeds the old one + // i.e. if there are any hx-on:eventName attributes, the hx-on attribute will be ignored + forEach(findHxOnWildcardElements(elt), processHxOnWildcard); + } + + //==================================================================== + // Event/Log Support + //==================================================================== + + function kebabEventName(str) { + return str.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase(); + } + + function makeEvent(eventName, detail) { + var evt; + if (window.CustomEvent && typeof window.CustomEvent === 'function') { + evt = new CustomEvent(eventName, { bubbles: true, cancelable: true, detail: detail }); + } else { + evt = getDocument().createEvent('CustomEvent'); + evt.initCustomEvent(eventName, true, true, detail); + } + return evt; + } + + function triggerErrorEvent(elt, eventName, detail) { + triggerEvent(elt, eventName, mergeObjects({ error: eventName }, detail)); + } + + function ignoreEventForLogging(eventName) { + return eventName === "htmx:afterProcessNode" + } + + /** + * `withExtensions` locates all active extensions for a provided element, then + * executes the provided function using each of the active extensions. It should + * be called internally at every extendable execution point in htmx. + * + * @param {HTMLElement} elt + * @param {(extension:import("./htmx").HtmxExtension) => void} toDo + * @returns void + */ + function withExtensions(elt, toDo) { + forEach(getExtensions(elt), function (extension) { + try { + toDo(extension); + } catch (e) { + logError(e); + } + }); + } + + function logError(msg) { + if (console.error) { + console.error(msg); + } else if (console.log) { + console.log("ERROR: ", msg); + } + } + + function triggerEvent(elt, eventName, detail) { + elt = resolveTarget(elt); + if (detail == null) { + detail = {}; + } + detail["elt"] = elt; + var event = makeEvent(eventName, detail); + if (htmx.logger && !ignoreEventForLogging(eventName)) { + htmx.logger(elt, eventName, detail); + } + if (detail.error) { + logError(detail.error); + triggerEvent(elt, "htmx:error", { errorInfo: detail }) + } + var eventResult = elt.dispatchEvent(event); + var kebabName = kebabEventName(eventName); + if (eventResult && kebabName !== eventName) { + var kebabedEvent = makeEvent(kebabName, event.detail); + eventResult = eventResult && elt.dispatchEvent(kebabedEvent) + } + withExtensions(elt, function (extension) { + eventResult = eventResult && (extension.onEvent(eventName, event) !== false && !event.defaultPrevented) + }); + return eventResult; + } + + //==================================================================== + // History Support + //==================================================================== + var currentPathForHistory = location.pathname + location.search; + + function getHistoryElement() { + var historyElt = getDocument().querySelector('[hx-history-elt],[data-hx-history-elt]'); + return historyElt || getDocument().body; + } + + function saveToHistoryCache(url, content, title, scroll) { + if (!canAccessLocalStorage()) { + return; + } + + if (htmx.config.historyCacheSize <= 0) { + // make sure that an eventually already existing cache is purged + localStorage.removeItem("htmx-history-cache"); + return; + } + + url = normalizePath(url); + + var historyCache = parseJSON(localStorage.getItem("htmx-history-cache")) || []; + for (var i = 0; i < historyCache.length; i++) { + if (historyCache[i].url === url) { + historyCache.splice(i, 1); + break; + } + } + var newHistoryItem = { url: url, content: content, title: title, scroll: scroll }; + triggerEvent(getDocument().body, "htmx:historyItemCreated", { item: newHistoryItem, cache: historyCache }) + historyCache.push(newHistoryItem) + while (historyCache.length > htmx.config.historyCacheSize) { + historyCache.shift(); + } + while (historyCache.length > 0) { + try { + localStorage.setItem("htmx-history-cache", JSON.stringify(historyCache)); + break; + } catch (e) { + triggerErrorEvent(getDocument().body, "htmx:historyCacheError", { cause: e, cache: historyCache }) + historyCache.shift(); // shrink the cache and retry + } + } + } + + function getCachedHistory(url) { + if (!canAccessLocalStorage()) { + return null; + } + + url = normalizePath(url); + + var historyCache = parseJSON(localStorage.getItem("htmx-history-cache")) || []; + for (var i = 0; i < historyCache.length; i++) { + if (historyCache[i].url === url) { + return historyCache[i]; + } + } + return null; + } + + function cleanInnerHtmlForHistory(elt) { + var className = htmx.config.requestClass; + var clone = elt.cloneNode(true); + forEach(findAll(clone, "." + className), function (child) { + removeClassFromElement(child, className); + }); + return clone.innerHTML; + } + + function saveCurrentPageToHistory() { + var elt = getHistoryElement(); + var path = currentPathForHistory || location.pathname + location.search; + + // Allow history snapshot feature to be disabled where hx-history="false" + // is present *anywhere* in the current document we're about to save, + // so we can prevent privileged data entering the cache. + // The page will still be reachable as a history entry, but htmx will fetch it + // live from the server onpopstate rather than look in the localStorage cache + var disableHistoryCache + try { + disableHistoryCache = getDocument().querySelector('[hx-history="false" i],[data-hx-history="false" i]') + } catch (e) { + // IE11: insensitive modifier not supported so fallback to case sensitive selector + disableHistoryCache = getDocument().querySelector('[hx-history="false"],[data-hx-history="false"]') + } + if (!disableHistoryCache) { + triggerEvent(getDocument().body, "htmx:beforeHistorySave", { path: path, historyElt: elt }); + saveToHistoryCache(path, cleanInnerHtmlForHistory(elt), getDocument().title, window.scrollY); + } + + if (htmx.config.historyEnabled) history.replaceState({ htmx: true }, getDocument().title, window.location.href); + } + + function pushUrlIntoHistory(path) { + // remove the cache buster parameter, if any + if (htmx.config.getCacheBusterParam) { + path = path.replace(/org\.htmx\.cache-buster=[^&]*&?/, '') + if (endsWith(path, '&') || endsWith(path, "?")) { + path = path.slice(0, -1); + } + } + if (htmx.config.historyEnabled) { + history.pushState({ htmx: true }, "", path); + } + currentPathForHistory = path; + } + + function replaceUrlInHistory(path) { + if (htmx.config.historyEnabled) history.replaceState({ htmx: true }, "", path); + currentPathForHistory = path; + } + + function settleImmediately(tasks) { + forEach(tasks, function (task) { + task.call(); + }); + } + + function loadHistoryFromServer(path) { + var request = new XMLHttpRequest(); + var details = { path: path, xhr: request }; + triggerEvent(getDocument().body, "htmx:historyCacheMiss", details); + request.open('GET', path, true); + request.setRequestHeader("HX-Request", "true"); + request.setRequestHeader("HX-History-Restore-Request", "true"); + request.setRequestHeader("HX-Current-URL", getDocument().location.href); + request.onload = function () { + if (this.status >= 200 && this.status < 400) { + triggerEvent(getDocument().body, "htmx:historyCacheMissLoad", details); + var fragment = makeFragment(this.response); + // @ts-ignore + fragment = fragment.querySelector('[hx-history-elt],[data-hx-history-elt]') || fragment; + var historyElement = getHistoryElement(); + var settleInfo = makeSettleInfo(historyElement); + var title = findTitle(this.response); + if (title) { + var titleElt = find("title"); + if (titleElt) { + titleElt.innerHTML = title; + } else { + window.document.title = title; + } + } + // @ts-ignore + swapInnerHTML(historyElement, fragment, settleInfo) + settleImmediately(settleInfo.tasks); + currentPathForHistory = path; + triggerEvent(getDocument().body, "htmx:historyRestore", { path: path, cacheMiss: true, serverResponse: this.response }); + } else { + triggerErrorEvent(getDocument().body, "htmx:historyCacheMissLoadError", details); + } + }; + request.send(); + } + + function restoreHistory(path) { + saveCurrentPageToHistory(); + path = path || location.pathname + location.search; + var cached = getCachedHistory(path); + if (cached) { + var fragment = makeFragment(cached.content); + var historyElement = getHistoryElement(); + var settleInfo = makeSettleInfo(historyElement); + swapInnerHTML(historyElement, fragment, settleInfo) + settleImmediately(settleInfo.tasks); + document.title = cached.title; + setTimeout(function () { + window.scrollTo(0, cached.scroll); + }, 0); // next 'tick', so browser has time to render layout + currentPathForHistory = path; + triggerEvent(getDocument().body, "htmx:historyRestore", { path: path, item: cached }); + } else { + if (htmx.config.refreshOnHistoryMiss) { + + // @ts-ignore: optional parameter in reload() function throws error + window.location.reload(true); + } else { + loadHistoryFromServer(path); + } + } + } + + function addRequestIndicatorClasses(elt) { + var indicators = findAttributeTargets(elt, 'hx-indicator'); + if (indicators == null) { + indicators = [elt]; + } + forEach(indicators, function (ic) { + var internalData = getInternalData(ic); + internalData.requestCount = (internalData.requestCount || 0) + 1; + ic.classList["add"].call(ic.classList, htmx.config.requestClass); + }); + return indicators; + } + + function disableElements(elt) { + var disabledElts = findAttributeTargets(elt, 'hx-disabled-elt'); + if (disabledElts == null) { + disabledElts = []; + } + forEach(disabledElts, function (disabledElement) { + var internalData = getInternalData(disabledElement); + internalData.requestCount = (internalData.requestCount || 0) + 1; + disabledElement.setAttribute("disabled", ""); + }); + return disabledElts; + } + + function removeRequestIndicators(indicators, disabled) { + forEach(indicators, function (ic) { + var internalData = getInternalData(ic); + internalData.requestCount = (internalData.requestCount || 0) - 1; + if (internalData.requestCount === 0) { + ic.classList["remove"].call(ic.classList, htmx.config.requestClass); + } + }); + forEach(disabled, function (disabledElement) { + var internalData = getInternalData(disabledElement); + internalData.requestCount = (internalData.requestCount || 0) - 1; + if (internalData.requestCount === 0) { + disabledElement.removeAttribute('disabled'); + } + }); + } + + //==================================================================== + // Input Value Processing + //==================================================================== + + function haveSeenNode(processed, elt) { + for (var i = 0; i < processed.length; i++) { + var node = processed[i]; + if (node.isSameNode(elt)) { + return true; + } + } + return false; + } + + function shouldInclude(elt) { + if (elt.name === "" || elt.name == null || elt.disabled || closest(elt, "fieldset[disabled]")) { + return false; + } + // ignore "submitter" types (see jQuery src/serialize.js) + if (elt.type === "button" || elt.type === "submit" || elt.tagName === "image" || elt.tagName === "reset" || elt.tagName === "file") { + return false; + } + if (elt.type === "checkbox" || elt.type === "radio") { + return elt.checked; + } + return true; + } + + function addValueToValues(name, value, values) { + // This is a little ugly because both the current value of the named value in the form + // and the new value could be arrays, so we have to handle all four cases :/ + if (name != null && value != null) { + var current = values[name]; + if (current === undefined) { + values[name] = value; + } else if (Array.isArray(current)) { + if (Array.isArray(value)) { + values[name] = current.concat(value); + } else { + current.push(value); + } + } else { + if (Array.isArray(value)) { + values[name] = [current].concat(value); + } else { + values[name] = [current, value]; + } + } + } + } + + function processInputValue(processed, values, errors, elt, validate) { + if (elt == null || haveSeenNode(processed, elt)) { + return; + } else { + processed.push(elt); + } + if (shouldInclude(elt)) { + var name = getRawAttribute(elt, "name"); + var value = elt.value; + if (elt.multiple && elt.tagName === "SELECT") { + value = toArray(elt.querySelectorAll("option:checked")).map(function (e) { return e.value }); + } + // include file inputs + if (elt.files) { + value = toArray(elt.files); + } + addValueToValues(name, value, values); + if (validate) { + validateElement(elt, errors); + } + } + if (matches(elt, 'form')) { + var inputs = elt.elements; + forEach(inputs, function (input) { + processInputValue(processed, values, errors, input, validate); + }); + } + } + + function validateElement(element, errors) { + if (element.willValidate) { + triggerEvent(element, "htmx:validation:validate") + if (!element.checkValidity()) { + errors.push({ elt: element, message: element.validationMessage, validity: element.validity }); + triggerEvent(element, "htmx:validation:failed", { message: element.validationMessage, validity: element.validity }) + } + } + } + + /** + * @param {HTMLElement} elt + * @param {string} verb + */ + function getInputValues(elt, verb) { + var processed = []; + var values = {}; + var formValues = {}; + var errors = []; + var internalData = getInternalData(elt); + if (internalData.lastButtonClicked && !bodyContains(internalData.lastButtonClicked)) { + internalData.lastButtonClicked = null + } + + // only validate when form is directly submitted and novalidate or formnovalidate are not set + // or if the element has an explicit hx-validate="true" on it + var validate = (matches(elt, 'form') && elt.noValidate !== true) || getAttributeValue(elt, "hx-validate") === "true"; + if (internalData.lastButtonClicked) { + validate = validate && internalData.lastButtonClicked.formNoValidate !== true; + } + + // for a non-GET include the closest form + if (verb !== 'get') { + processInputValue(processed, formValues, errors, closest(elt, 'form'), validate); + } + + // include the element itself + processInputValue(processed, values, errors, elt, validate); + + // if a button or submit was clicked last, include its value + if (internalData.lastButtonClicked || elt.tagName === "BUTTON" || + (elt.tagName === "INPUT" && getRawAttribute(elt, "type") === "submit")) { + var button = internalData.lastButtonClicked || elt + var name = getRawAttribute(button, "name") + addValueToValues(name, button.value, formValues) + } + + // include any explicit includes + var includes = findAttributeTargets(elt, "hx-include"); + forEach(includes, function (node) { + processInputValue(processed, values, errors, node, validate); + // if a non-form is included, include any input values within it + if (!matches(node, 'form')) { + forEach(node.querySelectorAll(INPUT_SELECTOR), function (descendant) { + processInputValue(processed, values, errors, descendant, validate); + }) + } + }); + + // form values take precedence, overriding the regular values + values = mergeObjects(values, formValues); + + return { errors: errors, values: values }; + } + + function appendParam(returnStr, name, realValue) { + if (returnStr !== "") { + returnStr += "&"; + } + if (String(realValue) === "[object Object]") { + realValue = JSON.stringify(realValue); + } + var s = encodeURIComponent(realValue); + returnStr += encodeURIComponent(name) + "=" + s; + return returnStr; + } + + function urlEncode(values) { + var returnStr = ""; + for (var name in values) { + if (values.hasOwnProperty(name)) { + var value = values[name]; + if (Array.isArray(value)) { + forEach(value, function (v) { + returnStr = appendParam(returnStr, name, v); + }); + } else { + returnStr = appendParam(returnStr, name, value); + } + } + } + return returnStr; + } + + function makeFormData(values) { + var formData = new FormData(); + for (var name in values) { + if (values.hasOwnProperty(name)) { + var value = values[name]; + if (Array.isArray(value)) { + forEach(value, function (v) { + formData.append(name, v); + }); + } else { + formData.append(name, value); + } + } + } + return formData; + } + + //==================================================================== + // Ajax + //==================================================================== + + /** + * @param {HTMLElement} elt + * @param {HTMLElement} target + * @param {string} prompt + * @returns {Object} // TODO: Define/Improve HtmxHeaderSpecification + */ + function getHeaders(elt, target, prompt) { + var headers = { + "HX-Request": "true", + "HX-Trigger": getRawAttribute(elt, "id"), + "HX-Trigger-Name": getRawAttribute(elt, "name"), + "HX-Target": getAttributeValue(target, "id"), + "HX-Current-URL": getDocument().location.href, + } + getValuesForElement(elt, "hx-headers", false, headers) + if (prompt !== undefined) { + headers["HX-Prompt"] = prompt; + } + if (getInternalData(elt).boosted) { + headers["HX-Boosted"] = "true"; + } + return headers; + } + + /** + * filterValues takes an object containing form input values + * and returns a new object that only contains keys that are + * specified by the closest "hx-params" attribute + * @param {Object} inputValues + * @param {HTMLElement} elt + * @returns {Object} + */ + function filterValues(inputValues, elt) { + var paramsValue = getClosestAttributeValue(elt, "hx-params"); + if (paramsValue) { + if (paramsValue === "none") { + return {}; + } else if (paramsValue === "*") { + return inputValues; + } else if (paramsValue.indexOf("not ") === 0) { + forEach(paramsValue.substr(4).split(","), function (name) { + name = name.trim(); + delete inputValues[name]; + }); + return inputValues; + } else { + var newValues = {} + forEach(paramsValue.split(","), function (name) { + name = name.trim(); + newValues[name] = inputValues[name]; + }); + return newValues; + } + } else { + return inputValues; + } + } + + function isAnchorLink(elt) { + return getRawAttribute(elt, 'href') && getRawAttribute(elt, 'href').indexOf("#") >= 0 + } + + /** + * + * @param {HTMLElement} elt + * @param {string} swapInfoOverride + * @returns {import("./htmx").HtmxSwapSpecification} + */ + function getSwapSpecification(elt, swapInfoOverride) { + var swapInfo = swapInfoOverride ? swapInfoOverride : getClosestAttributeValue(elt, "hx-swap"); + var swapSpec = { + "swapStyle": getInternalData(elt).boosted ? 'innerHTML' : htmx.config.defaultSwapStyle, + "swapDelay": htmx.config.defaultSwapDelay, + "settleDelay": htmx.config.defaultSettleDelay + } + if (htmx.config.scrollIntoViewOnBoost && getInternalData(elt).boosted && !isAnchorLink(elt)) { + swapSpec["show"] = "top" + } + if (swapInfo) { + var split = splitOnWhitespace(swapInfo); + if (split.length > 0) { + for (var i = 0; i < split.length; i++) { + var value = split[i]; + if (value.indexOf("swap:") === 0) { + swapSpec["swapDelay"] = parseInterval(value.substr(5)); + } else if (value.indexOf("settle:") === 0) { + swapSpec["settleDelay"] = parseInterval(value.substr(7)); + } else if (value.indexOf("transition:") === 0) { + swapSpec["transition"] = value.substr(11) === "true"; + } else if (value.indexOf("ignoreTitle:") === 0) { + swapSpec["ignoreTitle"] = value.substr(12) === "true"; + } else if (value.indexOf("scroll:") === 0) { + var scrollSpec = value.substr(7); + var splitSpec = scrollSpec.split(":"); + var scrollVal = splitSpec.pop(); + var selectorVal = splitSpec.length > 0 ? splitSpec.join(":") : null; + swapSpec["scroll"] = scrollVal; + swapSpec["scrollTarget"] = selectorVal; + } else if (value.indexOf("show:") === 0) { + var showSpec = value.substr(5); + var splitSpec = showSpec.split(":"); + var showVal = splitSpec.pop(); + var selectorVal = splitSpec.length > 0 ? splitSpec.join(":") : null; + swapSpec["show"] = showVal; + swapSpec["showTarget"] = selectorVal; + } else if (value.indexOf("focus-scroll:") === 0) { + var focusScrollVal = value.substr("focus-scroll:".length); + swapSpec["focusScroll"] = focusScrollVal == "true"; + } else if (i == 0) { + swapSpec["swapStyle"] = value; + } else { + logError('Unknown modifier in hx-swap: ' + value); + } + } + } + } + return swapSpec; + } + + function usesFormData(elt) { + return getClosestAttributeValue(elt, "hx-encoding") === "multipart/form-data" || + (matches(elt, "form") && getRawAttribute(elt, 'enctype') === "multipart/form-data"); + } + + function encodeParamsForBody(xhr, elt, filteredParameters) { + var encodedParameters = null; + withExtensions(elt, function (extension) { + if (encodedParameters == null) { + encodedParameters = extension.encodeParameters(xhr, filteredParameters, elt); + } + }); + if (encodedParameters != null) { + return encodedParameters; + } else { + if (usesFormData(elt)) { + return makeFormData(filteredParameters); + } else { + return urlEncode(filteredParameters); + } + } + } + + /** + * + * @param {Element} target + * @returns {import("./htmx").HtmxSettleInfo} + */ + function makeSettleInfo(target) { + return { tasks: [], elts: [target] }; + } + + function updateScrollState(content, swapSpec) { + var first = content[0]; + var last = content[content.length - 1]; + if (swapSpec.scroll) { + var target = null; + if (swapSpec.scrollTarget) { + target = querySelectorExt(first, swapSpec.scrollTarget); + } + if (swapSpec.scroll === "top" && (first || target)) { + target = target || first; + target.scrollTop = 0; + } + if (swapSpec.scroll === "bottom" && (last || target)) { + target = target || last; + target.scrollTop = target.scrollHeight; + } + } + if (swapSpec.show) { + var target = null; + if (swapSpec.showTarget) { + var targetStr = swapSpec.showTarget; + if (swapSpec.showTarget === "window") { + targetStr = "body"; + } + target = querySelectorExt(first, targetStr); + } + if (swapSpec.show === "top" && (first || target)) { + target = target || first; + target.scrollIntoView({ block: 'start', behavior: htmx.config.scrollBehavior }); + } + if (swapSpec.show === "bottom" && (last || target)) { + target = target || last; + target.scrollIntoView({ block: 'end', behavior: htmx.config.scrollBehavior }); + } + } + } + + /** + * @param {HTMLElement} elt + * @param {string} attr + * @param {boolean=} evalAsDefault + * @param {Object=} values + * @returns {Object} + */ + function getValuesForElement(elt, attr, evalAsDefault, values) { + if (values == null) { + values = {}; + } + if (elt == null) { + return values; + } + var attributeValue = getAttributeValue(elt, attr); + if (attributeValue) { + var str = attributeValue.trim(); + var evaluateValue = evalAsDefault; + if (str === "unset") { + return null; + } + if (str.indexOf("javascript:") === 0) { + str = str.substr(11); + evaluateValue = true; + } else if (str.indexOf("js:") === 0) { + str = str.substr(3); + evaluateValue = true; + } + if (str.indexOf('{') !== 0) { + str = "{" + str + "}"; + } + var varsValues; + if (evaluateValue) { + varsValues = maybeEval(elt, function () { return Function("return (" + str + ")")(); }, {}); + } else { + varsValues = parseJSON(str); + } + for (var key in varsValues) { + if (varsValues.hasOwnProperty(key)) { + if (values[key] == null) { + values[key] = varsValues[key]; + } + } + } + } + return getValuesForElement(parentElt(elt), attr, evalAsDefault, values); + } + + function maybeEval(elt, toEval, defaultVal) { + if (htmx.config.allowEval) { + return toEval(); + } else { + triggerErrorEvent(elt, 'htmx:evalDisallowedError'); + return defaultVal; + } + } + + /** + * @param {HTMLElement} elt + * @param {*} expressionVars + * @returns + */ + function getHXVarsForElement(elt, expressionVars) { + return getValuesForElement(elt, "hx-vars", true, expressionVars); + } + + /** + * @param {HTMLElement} elt + * @param {*} expressionVars + * @returns + */ + function getHXValsForElement(elt, expressionVars) { + return getValuesForElement(elt, "hx-vals", false, expressionVars); + } + + /** + * @param {HTMLElement} elt + * @returns {Object} + */ + function getExpressionVars(elt) { + return mergeObjects(getHXVarsForElement(elt), getHXValsForElement(elt)); + } + + function safelySetHeaderValue(xhr, header, headerValue) { + if (headerValue !== null) { + try { + xhr.setRequestHeader(header, headerValue); + } catch (e) { + // On an exception, try to set the header URI encoded instead + xhr.setRequestHeader(header, encodeURIComponent(headerValue)); + xhr.setRequestHeader(header + "-URI-AutoEncoded", "true"); + } + } + } + + function getPathFromResponse(xhr) { + // NB: IE11 does not support this stuff + if (xhr.responseURL && typeof (URL) !== "undefined") { + try { + var url = new URL(xhr.responseURL); + return url.pathname + url.search; + } catch (e) { + triggerErrorEvent(getDocument().body, "htmx:badResponseUrl", { url: xhr.responseURL }); + } + } + } + + function hasHeader(xhr, regexp) { + return regexp.test(xhr.getAllResponseHeaders()) + } + + function ajaxHelper(verb, path, context) { + verb = verb.toLowerCase(); + if (context) { + if (context instanceof Element || isType(context, 'String')) { + return issueAjaxRequest(verb, path, null, null, { + targetOverride: resolveTarget(context), + returnPromise: true + }); + } else { + return issueAjaxRequest(verb, path, resolveTarget(context.source), context.event, + { + handler: context.handler, + headers: context.headers, + values: context.values, + targetOverride: resolveTarget(context.target), + swapOverride: context.swap, + select: context.select, + returnPromise: true + }); + } + } else { + return issueAjaxRequest(verb, path, null, null, { + returnPromise: true + }); + } + } + + function hierarchyForElt(elt) { + var arr = []; + while (elt) { + arr.push(elt); + elt = elt.parentElement; + } + return arr; + } + + function verifyPath(elt, path, requestConfig) { + var sameHost + var url + if (typeof URL === "function") { + url = new URL(path, document.location.href); + var origin = document.location.origin; + sameHost = origin === url.origin; + } else { + // IE11 doesn't support URL + url = path + sameHost = startsWith(path, document.location.origin) + } + + if (htmx.config.selfRequestsOnly) { + if (!sameHost) { + return false; + } + } + return triggerEvent(elt, "htmx:validateUrl", mergeObjects({ url: url, sameHost: sameHost }, requestConfig)); + } + + function issueAjaxRequest(verb, path, elt, event, etc, confirmed) { + var resolve = null; + var reject = null; + etc = etc != null ? etc : {}; + if (etc.returnPromise && typeof Promise !== "undefined") { + var promise = new Promise(function (_resolve, _reject) { + resolve = _resolve; + reject = _reject; + }); + } + if (elt == null) { + elt = getDocument().body; + } + var responseHandler = etc.handler || handleAjaxResponse; + var select = etc.select || null; + + if (!bodyContains(elt)) { + // do not issue requests for elements removed from the DOM + maybeCall(resolve); + return promise; + } + var target = etc.targetOverride || getTarget(elt); + if (target == null || target == DUMMY_ELT) { + triggerErrorEvent(elt, 'htmx:targetError', { target: getAttributeValue(elt, "hx-target") }); + maybeCall(reject); + return promise; + } + + var eltData = getInternalData(elt); + var submitter = eltData.lastButtonClicked; + + if (submitter) { + var buttonPath = getRawAttribute(submitter, "formaction"); + if (buttonPath != null) { + path = buttonPath; + } + + var buttonVerb = getRawAttribute(submitter, "formmethod") + if (buttonVerb != null) { + // ignore buttons with formmethod="dialog" + if (buttonVerb.toLowerCase() !== "dialog") { + verb = buttonVerb; + } + } + } + + var confirmQuestion = getClosestAttributeValue(elt, "hx-confirm"); + // allow event-based confirmation w/ a callback + if (confirmed === undefined) { + var issueRequest = function (skipConfirmation) { + return issueAjaxRequest(verb, path, elt, event, etc, !!skipConfirmation); + } + var confirmDetails = { target: target, elt: elt, path: path, verb: verb, triggeringEvent: event, etc: etc, issueRequest: issueRequest, question: confirmQuestion }; + if (triggerEvent(elt, 'htmx:confirm', confirmDetails) === false) { + maybeCall(resolve); + return promise; + } + } + + var syncElt = elt; + var syncStrategy = getClosestAttributeValue(elt, "hx-sync"); + var queueStrategy = null; + var abortable = false; + if (syncStrategy) { + var syncStrings = syncStrategy.split(":"); + var selector = syncStrings[0].trim(); + if (selector === "this") { + syncElt = findThisElement(elt, 'hx-sync'); + } else { + syncElt = querySelectorExt(elt, selector); + } + // default to the drop strategy + syncStrategy = (syncStrings[1] || 'drop').trim(); + eltData = getInternalData(syncElt); + if (syncStrategy === "drop" && eltData.xhr && eltData.abortable !== true) { + maybeCall(resolve); + return promise; + } else if (syncStrategy === "abort") { + if (eltData.xhr) { + maybeCall(resolve); + return promise; + } else { + abortable = true; + } + } else if (syncStrategy === "replace") { + triggerEvent(syncElt, 'htmx:abort'); // abort the current request and continue + } else if (syncStrategy.indexOf("queue") === 0) { + var queueStrArray = syncStrategy.split(" "); + queueStrategy = (queueStrArray[1] || "last").trim(); + } + } + + if (eltData.xhr) { + if (eltData.abortable) { + triggerEvent(syncElt, 'htmx:abort'); // abort the current request and continue + } else { + if (queueStrategy == null) { + if (event) { + var eventData = getInternalData(event); + if (eventData && eventData.triggerSpec && eventData.triggerSpec.queue) { + queueStrategy = eventData.triggerSpec.queue; + } + } + if (queueStrategy == null) { + queueStrategy = "last"; + } + } + if (eltData.queuedRequests == null) { + eltData.queuedRequests = []; + } + if (queueStrategy === "first" && eltData.queuedRequests.length === 0) { + eltData.queuedRequests.push(function () { + issueAjaxRequest(verb, path, elt, event, etc) + }); + } else if (queueStrategy === "all") { + eltData.queuedRequests.push(function () { + issueAjaxRequest(verb, path, elt, event, etc) + }); + } else if (queueStrategy === "last") { + eltData.queuedRequests = []; // dump existing queue + eltData.queuedRequests.push(function () { + issueAjaxRequest(verb, path, elt, event, etc) + }); + } + maybeCall(resolve); + return promise; + } + } + + var xhr = new XMLHttpRequest(); + eltData.xhr = xhr; + eltData.abortable = abortable; + var endRequestLock = function () { + eltData.xhr = null; + eltData.abortable = false; + if (eltData.queuedRequests != null && + eltData.queuedRequests.length > 0) { + var queuedRequest = eltData.queuedRequests.shift(); + queuedRequest(); + } + } + var promptQuestion = getClosestAttributeValue(elt, "hx-prompt"); + if (promptQuestion) { + var promptResponse = prompt(promptQuestion); + // prompt returns null if cancelled and empty string if accepted with no entry + if (promptResponse === null || + !triggerEvent(elt, 'htmx:prompt', { prompt: promptResponse, target: target })) { + maybeCall(resolve); + endRequestLock(); + return promise; + } + } + + if (confirmQuestion && !confirmed) { + if (!confirm(confirmQuestion)) { + maybeCall(resolve); + endRequestLock() + return promise; + } + } + + + var headers = getHeaders(elt, target, promptResponse); + + if (verb !== 'get' && !usesFormData(elt)) { + headers['Content-Type'] = 'application/x-www-form-urlencoded'; + } + + if (etc.headers) { + headers = mergeObjects(headers, etc.headers); + } + var results = getInputValues(elt, verb); + var errors = results.errors; + var rawParameters = results.values; + if (etc.values) { + rawParameters = mergeObjects(rawParameters, etc.values); + } + var expressionVars = getExpressionVars(elt); + var allParameters = mergeObjects(rawParameters, expressionVars); + var filteredParameters = filterValues(allParameters, elt); + + if (htmx.config.getCacheBusterParam && verb === 'get') { + filteredParameters['org.htmx.cache-buster'] = getRawAttribute(target, "id") || "true"; + } + + // behavior of anchors w/ empty href is to use the current URL + if (path == null || path === "") { + path = getDocument().location.href; + } + + + var requestAttrValues = getValuesForElement(elt, 'hx-request'); + + var eltIsBoosted = getInternalData(elt).boosted; + + var useUrlParams = htmx.config.methodsThatUseUrlParams.indexOf(verb) >= 0 + + var requestConfig = { + boosted: eltIsBoosted, + useUrlParams: useUrlParams, + parameters: filteredParameters, + unfilteredParameters: allParameters, + headers: headers, + target: target, + verb: verb, + errors: errors, + withCredentials: etc.credentials || requestAttrValues.credentials || htmx.config.withCredentials, + timeout: etc.timeout || requestAttrValues.timeout || htmx.config.timeout, + path: path, + triggeringEvent: event + }; + + if (!triggerEvent(elt, 'htmx:configRequest', requestConfig)) { + maybeCall(resolve); + endRequestLock(); + return promise; + } + + // copy out in case the object was overwritten + path = requestConfig.path; + verb = requestConfig.verb; + headers = requestConfig.headers; + filteredParameters = requestConfig.parameters; + errors = requestConfig.errors; + useUrlParams = requestConfig.useUrlParams; + + if (errors && errors.length > 0) { + triggerEvent(elt, 'htmx:validation:halted', requestConfig) + maybeCall(resolve); + endRequestLock(); + return promise; + } + + var splitPath = path.split("#"); + var pathNoAnchor = splitPath[0]; + var anchor = splitPath[1]; + + var finalPath = path + if (useUrlParams) { + finalPath = pathNoAnchor; + var values = Object.keys(filteredParameters).length !== 0; + if (values) { + if (finalPath.indexOf("?") < 0) { + finalPath += "?"; + } else { + finalPath += "&"; + } + finalPath += urlEncode(filteredParameters); + if (anchor) { + finalPath += "#" + anchor; + } + } + } + + if (!verifyPath(elt, finalPath, requestConfig)) { + triggerErrorEvent(elt, 'htmx:invalidPath', requestConfig) + maybeCall(reject); + return promise; + }; + + xhr.open(verb.toUpperCase(), finalPath, true); + xhr.overrideMimeType("text/html"); + xhr.withCredentials = requestConfig.withCredentials; + xhr.timeout = requestConfig.timeout; + + // request headers + if (requestAttrValues.noHeaders) { + // ignore all headers + } else { + for (var header in headers) { + if (headers.hasOwnProperty(header)) { + var headerValue = headers[header]; + safelySetHeaderValue(xhr, header, headerValue); + } + } + } + + var responseInfo = { + xhr: xhr, target: target, requestConfig: requestConfig, etc: etc, boosted: eltIsBoosted, select: select, + pathInfo: { + requestPath: path, + finalRequestPath: finalPath, + anchor: anchor + } + }; + + xhr.onload = function () { + try { + var hierarchy = hierarchyForElt(elt); + responseInfo.pathInfo.responsePath = getPathFromResponse(xhr); + responseHandler(elt, responseInfo); + removeRequestIndicators(indicators, disableElts); + triggerEvent(elt, 'htmx:afterRequest', responseInfo); + triggerEvent(elt, 'htmx:afterOnLoad', responseInfo); + // if the body no longer contains the element, trigger the event on the closest parent + // remaining in the DOM + if (!bodyContains(elt)) { + var secondaryTriggerElt = null; + while (hierarchy.length > 0 && secondaryTriggerElt == null) { + var parentEltInHierarchy = hierarchy.shift(); + if (bodyContains(parentEltInHierarchy)) { + secondaryTriggerElt = parentEltInHierarchy; + } + } + if (secondaryTriggerElt) { + triggerEvent(secondaryTriggerElt, 'htmx:afterRequest', responseInfo); + triggerEvent(secondaryTriggerElt, 'htmx:afterOnLoad', responseInfo); + } + } + maybeCall(resolve); + endRequestLock(); + } catch (e) { + triggerErrorEvent(elt, 'htmx:onLoadError', mergeObjects({ error: e }, responseInfo)); + throw e; + } + } + xhr.onerror = function () { + removeRequestIndicators(indicators, disableElts); + triggerErrorEvent(elt, 'htmx:afterRequest', responseInfo); + triggerErrorEvent(elt, 'htmx:sendError', responseInfo); + maybeCall(reject); + endRequestLock(); + } + xhr.onabort = function () { + removeRequestIndicators(indicators, disableElts); + triggerErrorEvent(elt, 'htmx:afterRequest', responseInfo); + triggerErrorEvent(elt, 'htmx:sendAbort', responseInfo); + maybeCall(reject); + endRequestLock(); + } + xhr.ontimeout = function () { + removeRequestIndicators(indicators, disableElts); + triggerErrorEvent(elt, 'htmx:afterRequest', responseInfo); + triggerErrorEvent(elt, 'htmx:timeout', responseInfo); + maybeCall(reject); + endRequestLock(); + } + if (!triggerEvent(elt, 'htmx:beforeRequest', responseInfo)) { + maybeCall(resolve); + endRequestLock() + return promise + } + var indicators = addRequestIndicatorClasses(elt); + var disableElts = disableElements(elt); + + forEach(['loadstart', 'loadend', 'progress', 'abort'], function (eventName) { + forEach([xhr, xhr.upload], function (target) { + target.addEventListener(eventName, function (event) { + triggerEvent(elt, "htmx:xhr:" + eventName, { + lengthComputable: event.lengthComputable, + loaded: event.loaded, + total: event.total + }); + }) + }); + }); + triggerEvent(elt, 'htmx:beforeSend', responseInfo); + var params = useUrlParams ? null : encodeParamsForBody(xhr, elt, filteredParameters) + xhr.send(params); + return promise; + } + + function determineHistoryUpdates(elt, responseInfo) { + + var xhr = responseInfo.xhr; + + //=========================================== + // First consult response headers + //=========================================== + var pathFromHeaders = null; + var typeFromHeaders = null; + if (hasHeader(xhr, /HX-Push:/i)) { + pathFromHeaders = xhr.getResponseHeader("HX-Push"); + typeFromHeaders = "push"; + } else if (hasHeader(xhr, /HX-Push-Url:/i)) { + pathFromHeaders = xhr.getResponseHeader("HX-Push-Url"); + typeFromHeaders = "push"; + } else if (hasHeader(xhr, /HX-Replace-Url:/i)) { + pathFromHeaders = xhr.getResponseHeader("HX-Replace-Url"); + typeFromHeaders = "replace"; + } + + // if there was a response header, that has priority + if (pathFromHeaders) { + if (pathFromHeaders === "false") { + return {} + } else { + return { + type: typeFromHeaders, + path: pathFromHeaders + } + } + } + + //=========================================== + // Next resolve via DOM values + //=========================================== + var requestPath = responseInfo.pathInfo.finalRequestPath; + var responsePath = responseInfo.pathInfo.responsePath; + + var pushUrl = getClosestAttributeValue(elt, "hx-push-url"); + var replaceUrl = getClosestAttributeValue(elt, "hx-replace-url"); + var elementIsBoosted = getInternalData(elt).boosted; + + var saveType = null; + var path = null; + + if (pushUrl) { + saveType = "push"; + path = pushUrl; + } else if (replaceUrl) { + saveType = "replace"; + path = replaceUrl; + } else if (elementIsBoosted) { + saveType = "push"; + path = responsePath || requestPath; // if there is no response path, go with the original request path + } + + if (path) { + // false indicates no push, return empty object + if (path === "false") { + return {}; + } + + // true indicates we want to follow wherever the server ended up sending us + if (path === "true") { + path = responsePath || requestPath; // if there is no response path, go with the original request path + } + + // restore any anchor associated with the request + if (responseInfo.pathInfo.anchor && + path.indexOf("#") === -1) { + path = path + "#" + responseInfo.pathInfo.anchor; + } + + return { + type: saveType, + path: path + } + } else { + return {}; + } + } + + function handleAjaxResponse(elt, responseInfo) { + var xhr = responseInfo.xhr; + var target = responseInfo.target; + var etc = responseInfo.etc; + var requestConfig = responseInfo.requestConfig; + var select = responseInfo.select; + + if (!triggerEvent(elt, 'htmx:beforeOnLoad', responseInfo)) return; + + if (hasHeader(xhr, /HX-Trigger:/i)) { + handleTrigger(xhr, "HX-Trigger", elt); + } + + if (hasHeader(xhr, /HX-Location:/i)) { + saveCurrentPageToHistory(); + var redirectPath = xhr.getResponseHeader("HX-Location"); + var swapSpec; + if (redirectPath.indexOf("{") === 0) { + swapSpec = parseJSON(redirectPath); + // what's the best way to throw an error if the user didn't include this + redirectPath = swapSpec['path']; + delete swapSpec['path']; + } + ajaxHelper('GET', redirectPath, swapSpec).then(function () { + pushUrlIntoHistory(redirectPath); + }); + return; + } + + var shouldRefresh = hasHeader(xhr, /HX-Refresh:/i) && "true" === xhr.getResponseHeader("HX-Refresh"); + + if (hasHeader(xhr, /HX-Redirect:/i)) { + location.href = xhr.getResponseHeader("HX-Redirect"); + shouldRefresh && location.reload(); + return; + } + + if (shouldRefresh) { + location.reload(); + return; + } + + if (hasHeader(xhr, /HX-Retarget:/i)) { + if (xhr.getResponseHeader("HX-Retarget") === "this") { + responseInfo.target = elt; + } else { + responseInfo.target = querySelectorExt(elt, xhr.getResponseHeader("HX-Retarget")); + } + } + + var historyUpdate = determineHistoryUpdates(elt, responseInfo); + + // by default htmx only swaps on 200 return codes and does not swap + // on 204 'No Content' + // this can be ovverriden by responding to the htmx:beforeSwap event and + // overriding the detail.shouldSwap property + var shouldSwap = xhr.status >= 200 && xhr.status < 400 && xhr.status !== 204; + var serverResponse = xhr.response; + var isError = xhr.status >= 400; + var ignoreTitle = htmx.config.ignoreTitle + var beforeSwapDetails = mergeObjects({ shouldSwap: shouldSwap, serverResponse: serverResponse, isError: isError, ignoreTitle: ignoreTitle }, responseInfo); + if (!triggerEvent(target, 'htmx:beforeSwap', beforeSwapDetails)) return; + + target = beforeSwapDetails.target; // allow re-targeting + serverResponse = beforeSwapDetails.serverResponse; // allow updating content + isError = beforeSwapDetails.isError; // allow updating error + ignoreTitle = beforeSwapDetails.ignoreTitle; // allow updating ignoring title + + responseInfo.target = target; // Make updated target available to response events + responseInfo.failed = isError; // Make failed property available to response events + responseInfo.successful = !isError; // Make successful property available to response events + + if (beforeSwapDetails.shouldSwap) { + if (xhr.status === 286) { + cancelPolling(elt); + } + + withExtensions(elt, function (extension) { + serverResponse = extension.transformResponse(serverResponse, xhr, elt); + }); + + // Save current page if there will be a history update + if (historyUpdate.type) { + saveCurrentPageToHistory(); + } + + var swapOverride = etc.swapOverride; + if (hasHeader(xhr, /HX-Reswap:/i)) { + swapOverride = xhr.getResponseHeader("HX-Reswap"); + } + var swapSpec = getSwapSpecification(elt, swapOverride); + + if (swapSpec.hasOwnProperty('ignoreTitle')) { + ignoreTitle = swapSpec.ignoreTitle; + } + + target.classList.add(htmx.config.swappingClass); + + // optional transition API promise callbacks + var settleResolve = null; + var settleReject = null; + + var doSwap = function () { + try { + var activeElt = document.activeElement; + var selectionInfo = {}; + try { + selectionInfo = { + elt: activeElt, + // @ts-ignore + start: activeElt ? activeElt.selectionStart : null, + // @ts-ignore + end: activeElt ? activeElt.selectionEnd : null + }; + } catch (e) { + // safari issue - see https://github.com/microsoft/playwright/issues/5894 + } + + var selectOverride; + if (select) { + selectOverride = select; + } + + if (hasHeader(xhr, /HX-Reselect:/i)) { + selectOverride = xhr.getResponseHeader("HX-Reselect"); + } + + // if we need to save history, do so, before swapping so that relative resources have the correct base URL + if (historyUpdate.type) { + triggerEvent(getDocument().body, 'htmx:beforeHistoryUpdate', mergeObjects({ history: historyUpdate }, responseInfo)); + if (historyUpdate.type === "push") { + pushUrlIntoHistory(historyUpdate.path); + triggerEvent(getDocument().body, 'htmx:pushedIntoHistory', { path: historyUpdate.path }); + } else { + replaceUrlInHistory(historyUpdate.path); + triggerEvent(getDocument().body, 'htmx:replacedInHistory', { path: historyUpdate.path }); + } + } + + var settleInfo = makeSettleInfo(target); + selectAndSwap(swapSpec.swapStyle, target, elt, serverResponse, settleInfo, selectOverride); + + if (selectionInfo.elt && + !bodyContains(selectionInfo.elt) && + getRawAttribute(selectionInfo.elt, "id")) { + var newActiveElt = document.getElementById(getRawAttribute(selectionInfo.elt, "id")); + var focusOptions = { preventScroll: swapSpec.focusScroll !== undefined ? !swapSpec.focusScroll : !htmx.config.defaultFocusScroll }; + if (newActiveElt) { + // @ts-ignore + if (selectionInfo.start && newActiveElt.setSelectionRange) { + // @ts-ignore + try { + newActiveElt.setSelectionRange(selectionInfo.start, selectionInfo.end); + } catch (e) { + // the setSelectionRange method is present on fields that don't support it, so just let this fail + } + } + newActiveElt.focus(focusOptions); + } + } + + target.classList.remove(htmx.config.swappingClass); + forEach(settleInfo.elts, function (elt) { + if (elt.classList) { + elt.classList.add(htmx.config.settlingClass); + } + triggerEvent(elt, 'htmx:afterSwap', responseInfo); + }); + + if (hasHeader(xhr, /HX-Trigger-After-Swap:/i)) { + var finalElt = elt; + if (!bodyContains(elt)) { + finalElt = getDocument().body; + } + handleTrigger(xhr, "HX-Trigger-After-Swap", finalElt); + } + + var doSettle = function () { + forEach(settleInfo.tasks, function (task) { + task.call(); + }); + forEach(settleInfo.elts, function (elt) { + if (elt.classList) { + elt.classList.remove(htmx.config.settlingClass); + } + triggerEvent(elt, 'htmx:afterSettle', responseInfo); + }); + + if (responseInfo.pathInfo.anchor) { + var anchorTarget = getDocument().getElementById(responseInfo.pathInfo.anchor); + if (anchorTarget) { + anchorTarget.scrollIntoView({ block: 'start', behavior: "auto" }); + } + } + + if (settleInfo.title && !ignoreTitle) { + var titleElt = find("title"); + if (titleElt) { + titleElt.innerHTML = settleInfo.title; + } else { + window.document.title = settleInfo.title; + } + } + + updateScrollState(settleInfo.elts, swapSpec); + + if (hasHeader(xhr, /HX-Trigger-After-Settle:/i)) { + var finalElt = elt; + if (!bodyContains(elt)) { + finalElt = getDocument().body; + } + handleTrigger(xhr, "HX-Trigger-After-Settle", finalElt); + } + maybeCall(settleResolve); + } + + if (swapSpec.settleDelay > 0) { + setTimeout(doSettle, swapSpec.settleDelay) + } else { + doSettle(); + } + } catch (e) { + triggerErrorEvent(elt, 'htmx:swapError', responseInfo); + maybeCall(settleReject); + throw e; + } + }; + + var shouldTransition = htmx.config.globalViewTransitions + if (swapSpec.hasOwnProperty('transition')) { + shouldTransition = swapSpec.transition; + } + + if (shouldTransition && + triggerEvent(elt, 'htmx:beforeTransition', responseInfo) && + typeof Promise !== "undefined" && document.startViewTransition) { + var settlePromise = new Promise(function (_resolve, _reject) { + settleResolve = _resolve; + settleReject = _reject; + }); + // wrap the original doSwap() in a call to startViewTransition() + var innerDoSwap = doSwap; + doSwap = function () { + document.startViewTransition(function () { + innerDoSwap(); + return settlePromise; + }); + } + } + + + if (swapSpec.swapDelay > 0) { + setTimeout(doSwap, swapSpec.swapDelay) + } else { + doSwap(); + } + } + if (isError) { + triggerErrorEvent(elt, 'htmx:responseError', mergeObjects({ error: "Response Status Error Code " + xhr.status + " from " + responseInfo.pathInfo.requestPath }, responseInfo)); + } + } + + //==================================================================== + // Extensions API + //==================================================================== + + /** @type {Object} */ + var extensions = {}; + + /** + * extensionBase defines the default functions for all extensions. + * @returns {import("./htmx").HtmxExtension} + */ + function extensionBase() { + return { + init: function (api) { return null; }, + onEvent: function (name, evt) { return true; }, + transformResponse: function (text, xhr, elt) { return text; }, + isInlineSwap: function (swapStyle) { return false; }, + handleSwap: function (swapStyle, target, fragment, settleInfo) { return false; }, + encodeParameters: function (xhr, parameters, elt) { return null; } + } + } + + /** + * defineExtension initializes the extension and adds it to the htmx registry + * + * @param {string} name + * @param {import("./htmx").HtmxExtension} extension + */ + function defineExtension(name, extension) { + if (extension.init) { + extension.init(internalAPI) + } + extensions[name] = mergeObjects(extensionBase(), extension); + } + + /** + * removeExtension removes an extension from the htmx registry + * + * @param {string} name + */ + function removeExtension(name) { + delete extensions[name]; + } + + /** + * getExtensions searches up the DOM tree to return all extensions that can be applied to a given element + * + * @param {HTMLElement} elt + * @param {import("./htmx").HtmxExtension[]=} extensionsToReturn + * @param {import("./htmx").HtmxExtension[]=} extensionsToIgnore + */ + function getExtensions(elt, extensionsToReturn, extensionsToIgnore) { + + if (elt == undefined) { + return extensionsToReturn; + } + if (extensionsToReturn == undefined) { + extensionsToReturn = []; + } + if (extensionsToIgnore == undefined) { + extensionsToIgnore = []; + } + var extensionsForElement = getAttributeValue(elt, "hx-ext"); + if (extensionsForElement) { + forEach(extensionsForElement.split(","), function (extensionName) { + extensionName = extensionName.replace(/ /g, ''); + if (extensionName.slice(0, 7) == "ignore:") { + extensionsToIgnore.push(extensionName.slice(7)); + return; + } + if (extensionsToIgnore.indexOf(extensionName) < 0) { + var extension = extensions[extensionName]; + if (extension && extensionsToReturn.indexOf(extension) < 0) { + extensionsToReturn.push(extension); + } + } + }); + } + return getExtensions(parentElt(elt), extensionsToReturn, extensionsToIgnore); + } + + //==================================================================== + // Initialization + //==================================================================== + var isReady = false + getDocument().addEventListener('DOMContentLoaded', function () { + isReady = true + }) + + /** + * Execute a function now if DOMContentLoaded has fired, otherwise listen for it. + * + * This function uses isReady because there is no realiable way to ask the browswer whether + * the DOMContentLoaded event has already been fired; there's a gap between DOMContentLoaded + * firing and readystate=complete. + */ + function ready(fn) { + // Checking readyState here is a failsafe in case the htmx script tag entered the DOM by + // some means other than the initial page load. + if (isReady || getDocument().readyState === 'complete') { + fn(); + } else { + getDocument().addEventListener('DOMContentLoaded', fn); + } + } + + function insertIndicatorStyles() { + if (htmx.config.includeIndicatorStyles !== false) { + getDocument().head.insertAdjacentHTML("beforeend", + ""); + } + } + + function getMetaConfig() { + var element = getDocument().querySelector('meta[name="htmx-config"]'); + if (element) { + // @ts-ignore + return parseJSON(element.content); + } else { + return null; + } + } + + function mergeMetaConfig() { + var metaConfig = getMetaConfig(); + if (metaConfig) { + htmx.config = mergeObjects(htmx.config, metaConfig) + } + } + + // initialize the document + ready(function () { + mergeMetaConfig(); + insertIndicatorStyles(); + var body = getDocument().body; + processNode(body); + var restoredElts = getDocument().querySelectorAll( + "[hx-trigger='restored'],[data-hx-trigger='restored']" + ); + body.addEventListener("htmx:abort", function (evt) { + var target = evt.target; + var internalData = getInternalData(target); + if (internalData && internalData.xhr) { + internalData.xhr.abort(); + } + }); + /** @type {(ev: PopStateEvent) => any} */ + const originalPopstate = window.onpopstate ? window.onpopstate.bind(window) : null; + /** @type {(ev: PopStateEvent) => any} */ + window.onpopstate = function (event) { + if (event.state && event.state.htmx) { + restoreHistory(); + forEach(restoredElts, function (elt) { + triggerEvent(elt, 'htmx:restored', { + 'document': getDocument(), + 'triggerEvent': triggerEvent + }); + }); + } else { + if (originalPopstate) { + originalPopstate(event); + } + } + }; + setTimeout(function () { + triggerEvent(body, 'htmx:load', {}); // give ready handlers a chance to load up before firing this event + body = null; // kill reference for gc + }, 0); + }) + + return htmx; + } + )() +})); \ No newline at end of file diff --git a/vendor/github.com/darklab8/fl-darkstat/darkcore/core_static/htmx.1.9.11.preload.js b/vendor/github.com/darklab8/fl-darkstat/darkcore/core_static/htmx.1.9.11.preload.js new file mode 100644 index 0000000..a749370 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/darkcore/core_static/htmx.1.9.11.preload.js @@ -0,0 +1,147 @@ +// This adds the "preload" extension to htmx. By default, this will +// preload the targets of any tags with `href` or `hx-get` attributes +// if they also have a `preload` attribute as well. See documentation +// for more details +htmx.defineExtension("preload", { + + onEvent: function(name, event) { + + // Only take actions on "htmx:afterProcessNode" + if (name !== "htmx:afterProcessNode") { + return; + } + + // SOME HELPER FUNCTIONS WE'LL NEED ALONG THE WAY + + // attr gets the closest non-empty value from the attribute. + var attr = function(node, property) { + if (node == undefined) {return undefined;} + return node.getAttribute(property) || node.getAttribute("data-" + property) || attr(node.parentElement, property) + } + + // load handles the actual HTTP fetch, and uses htmx.ajax in cases where we're + // preloading an htmx resource (this sends the same HTTP headers as a regular htmx request) + var load = function(node) { + + // Called after a successful AJAX request, to mark the + // content as loaded (and prevent additional AJAX calls.) + var done = function(html) { + if (!node.preloadAlways) { + node.preloadState = "DONE" + } + + if (attr(node, "preload-images") == "true") { + document.createElement("div").innerHTML = html // create and populate a node to load linked resources, too. + } + } + + return function() { + + // If this value has already been loaded, then do not try again. + if (node.preloadState !== "READY") { + return; + } + + // Special handling for HX-GET - use built-in htmx.ajax function + // so that headers match other htmx requests, then set + // node.preloadState = TRUE so that requests are not duplicated + // in the future + var hxGet = node.getAttribute("hx-get") || node.getAttribute("data-hx-get") + if (hxGet) { + htmx.ajax("GET", hxGet, { + source: node, + handler:function(elt, info) { + done(info.xhr.responseText); + } + }); + return; + } + + // Otherwise, perform a standard xhr request, then set + // node.preloadState = TRUE so that requests are not duplicated + // in the future. + if (node.getAttribute("href")) { + var r = new XMLHttpRequest(); + r.open("GET", node.getAttribute("href")); + r.onload = function() {done(r.responseText);}; + r.send(); + return; + } + } + } + + // This function processes a specific node and sets up event handlers. + // We'll search for nodes and use it below. + var init = function(node) { + + // If this node DOES NOT include a "GET" transaction, then there's nothing to do here. + if (node.getAttribute("href") + node.getAttribute("hx-get") + node.getAttribute("data-hx-get") == "") { + return; + } + + // Guarantee that we only initialize each node once. + if (node.preloadState !== undefined) { + return; + } + + // Get event name from config. + var on = attr(node, "preload") || "mousedown" + const always = on.indexOf("always") !== -1 + if (always) { + on = on.replace('always', '').trim() + } + + // FALL THROUGH to here means we need to add an EventListener + + // Apply the listener to the node + node.addEventListener(on, function(evt) { + if (node.preloadState === "PAUSE") { // Only add one event listener + node.preloadState = "READY"; // Required for the `load` function to trigger + + // Special handling for "mouseover" events. Wait 100ms before triggering load. + if (on === "mouseover") { + window.setTimeout(load(node), 100); + } else { + load(node)() // all other events trigger immediately. + } + } + }) + + // Special handling for certain built-in event handlers + switch (on) { + + case "mouseover": + // Mirror `touchstart` events (fires immediately) + node.addEventListener("touchstart", load(node)); + + // WHhen the mouse leaves, immediately disable the preload + node.addEventListener("mouseout", function(evt) { + if ((evt.target === node) && (node.preloadState === "READY")) { + node.preloadState = "PAUSE"; + } + }) + break; + + case "mousedown": + // Mirror `touchstart` events (fires immediately) + node.addEventListener("touchstart", load(node)); + break; + } + + // Mark the node as ready to run. + node.preloadState = "PAUSE"; + node.preloadAlways = always; + htmx.trigger(node, "preload:init") // This event can be used to load content immediately. + } + + // Search for all child nodes that have a "preload" attribute + event.target.querySelectorAll("[preload]").forEach(function(node) { + + // Initialize the node with the "preload" attribute + init(node) + + // Initialize all child elements that are anchors or have `hx-get` (use with care) + node.querySelectorAll("a,[hx-get],[data-hx-get]").forEach(init) + }) + } +}) diff --git a/vendor/github.com/darklab8/fl-darkstat/darkcore/core_static/reset.css b/vendor/github.com/darklab8/fl-darkstat/darkcore/core_static/reset.css new file mode 100644 index 0000000..eef38ed --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/darkcore/core_static/reset.css @@ -0,0 +1,52 @@ +/* http://meyerweb.com/eric/tools/css/reset/ +v2.0 | 20110126 +License: none (public domain) +*/ + +html, body, div, span, applet, object, iframe, +h1, h2, h3, h4, h5, h6, p, blockquote, pre, +a, abbr, acronym, address, big, cite, code, +del, dfn, em, img, ins, kbd, q, s, samp, +small, strike, strong, sub, sup, tt, var, +b, u, i, center, +dl, dt, dd, ol, ul, li, +fieldset, form, label, legend, +table, caption, tbody, tfoot, thead, tr, th, td, +article, aside, canvas, details, embed, +figure, figcaption, footer, header, hgroup, +menu, nav, output, ruby, section, summary, +time, mark, audio, video { + margin: 0; + padding: 0; + border: 0; + font-size: 100%; + font: inherit; + font-family: Times New Roman, serif; + vertical-align: baseline; +} +button { + font-family: Arial, sans-serif; +} +/* HTML5 display-role reset for older browsers */ +article, aside, details, figcaption, figure, +footer, header, hgroup, menu, nav, section { + display: block; +} +body { + line-height: 1; +} +ol, ul { + list-style: none; +} +blockquote, q { + quotes: none; +} +blockquote:before, blockquote:after, +q:before, q:after { + content: ''; + content: none; +} +table { + border-collapse: collapse; + border-spacing: 0; +} diff --git a/vendor/github.com/darklab8/fl-darkstat/darkcore/core_static/sortable.js b/vendor/github.com/darklab8/fl-darkstat/darkcore/core_static/sortable.js new file mode 100644 index 0000000..dc6514b --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/darkcore/core_static/sortable.js @@ -0,0 +1,121 @@ +/** + * sortable v3.2.2 + * + * https://www.npmjs.com/package/sortable-tablesort + * https://github.com/tofsjonas/sortable + * + * Makes html tables sortable, No longer ie9+ 😢 + * + * Styling is done in css. + * + * Copyleft 2017 Jonas Earendel + * + * This is free and unencumbered software released into the public domain. + * + * Anyone is free to copy, modify, publish, use, compile, sell, or + * distribute this software, either in source code form or as a compiled + * binary, for any purpose, commercial or non-commercial, and by any + * means. + * + * In jurisdictions that recognize copyright laws, the author or authors + * of this software dedicate any and all copyright interest in the + * software to the public domain. We make this dedication for the benefit + * of the public at large and to the detriment of our heirs and + * successors. We intend this dedication to be an overt act of + * relinquishment in perpetuity of all present and future rights to this + * software under copyright law. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * For more information, please refer to + * + */ +document.addEventListener('click', function (e) { + try { + // allows for elements inside TH + function findElementRecursive(element, tag) { + return element.nodeName === tag ? element : findElementRecursive(element.parentNode, tag); + } + var ascending_table_sort_class = 'asc'; + var no_sort_class = 'no-sort'; + var null_last_class = 'n-last'; + var table_class_name = 'sortable'; + var alt_sort_1 = e.shiftKey || e.altKey; + var element = findElementRecursive(e.target, 'TH'); + var tr = element.parentNode; + var thead = tr.parentNode; + var table = thead.parentNode; + function getValue(element) { + var _a; + var value = alt_sort_1 ? element.dataset.sortAlt : (_a = element.dataset.sort) !== null && _a !== void 0 ? _a : element.textContent; + return value; + } + if (thead.nodeName === 'THEAD' && // sortable only triggered in `thead` + table.classList.contains(table_class_name) && + !element.classList.contains(no_sort_class) // .no-sort is now core functionality, no longer handled in CSS + ) { + var column_index_1; + var nodes = tr.cells; + var tiebreaker_1 = parseInt(element.dataset.sortTbr); + // Reset thead cells and get column index + for (var i = 0; i < nodes.length; i++) { + if (nodes[i] === element) { + column_index_1 = parseInt(element.dataset.sortCol) || i; + } + else { + nodes[i].setAttribute('aria-sort', 'none'); + } + } + var direction = 'descending'; + if (element.getAttribute('aria-sort') === 'descending' || + (table.classList.contains(ascending_table_sort_class) && element.getAttribute('aria-sort') !== 'ascending')) { + direction = 'ascending'; + } + // Update the `th` class accordingly + element.setAttribute('aria-sort', direction); + var reverse_1 = direction === 'ascending'; + var sort_null_last_1 = table.classList.contains(null_last_class); + var compare_1 = function (a, b, index) { + var x = getValue(b.cells[index]); + var y = getValue(a.cells[index]); + if (sort_null_last_1) { + if (x === '' && y !== '') { + return -1; + } + if (y === '' && x !== '') { + return 1; + } + } + var temp = Number(x) - Number(y); + var bool = isNaN(temp) ? x.localeCompare(y) : temp; + return reverse_1 ? -bool : bool; + }; + // loop through all tbodies and sort them + for (var i = 0; i < table.tBodies.length; i++) { + var org_tbody = table.tBodies[i]; + // Put the array rows in an array, so we can sort them... + var rows = [].slice.call(org_tbody.rows, 0); + // Sort them using Array.prototype.sort() + rows.sort(function (a, b) { + var bool = compare_1(a, b, column_index_1); + return bool === 0 && !isNaN(tiebreaker_1) ? compare_1(a, b, tiebreaker_1) : bool; + }); + // Make an empty clone + var clone_tbody = org_tbody.cloneNode(); + // Put the sorted rows inside the clone + clone_tbody.append.apply(clone_tbody, rows); + // And finally replace the unsorted tbody with the sorted one + table.replaceChild(clone_tbody, org_tbody); + } + } + } + catch (error) { + // console.log(error) + } +}); diff --git a/vendor/github.com/darklab8/fl-darkstat/darkcore/core_static/static.go b/vendor/github.com/darklab8/fl-darkstat/darkcore/core_static/static.go new file mode 100644 index 0000000..1be2b5e --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/darkcore/core_static/static.go @@ -0,0 +1,64 @@ +package core_static + +import ( + _ "embed" + + "github.com/darklab8/fl-darkstat/darkcore/core_types" +) + +// Commented out IE stuff as it makes things slow +// if (element.children) { // IE +// +// forEach(element.children, function(child) { cleanUpElement(child) }); +// } +// +// see https://github.com/bigskysoftware/htmx/issues/879 for more details +// +// also commented out // handleAttributes(parentNode, fragment, settleInfo); +// because we don't need CSS transitions and they are hurtful https://htmx.org/docs/#css_transitions +// + +//go:embed htmx.1.9.11.js +var HtmxJsContent string + +var HtmxJS core_types.StaticFile = core_types.StaticFile{ + Content: HtmxJsContent, + Filename: "htmx.js", + Kind: core_types.StaticFileJS, +} + +//go:embed htmx.1.9.11.preload.js +var PreloadJsContent string + +var HtmxPreloadJS core_types.StaticFile = core_types.StaticFile{ + Content: PreloadJsContent, + Filename: "htmx_preload.js", + Kind: core_types.StaticFileJS, +} + +//go:embed sortable.js +var SortableJsContent string + +var SortableJS core_types.StaticFile = core_types.StaticFile{ + Content: SortableJsContent, + Filename: "sortable.js", + Kind: core_types.StaticFileJS, +} + +//go:embed reset.css +var ResetCSSContent string + +var ResetCSS core_types.StaticFile = core_types.StaticFile{ + Content: ResetCSSContent, + Filename: "reset.css", + Kind: core_types.StaticFileCSS, +} + +//go:embed favicon.ico +var FaviconIcoContent string + +var FaviconIco core_types.StaticFile = core_types.StaticFile{ + Content: FaviconIcoContent, + Filename: "favicon.ico", + Kind: core_types.StaticFileIco, +} diff --git a/vendor/github.com/darklab8/fl-darkstat/darkcore/core_types/path_darwin.go b/vendor/github.com/darklab8/fl-darkstat/darkcore/core_types/path_darwin.go new file mode 100644 index 0000000..95ff6a2 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/darkcore/core_types/path_darwin.go @@ -0,0 +1,5 @@ +//go:build darwin + +package core_types + +const PATH_SEPARATOR = "/" diff --git a/vendor/github.com/darklab8/fl-darkstat/darkcore/core_types/path_linux.go b/vendor/github.com/darklab8/fl-darkstat/darkcore/core_types/path_linux.go new file mode 100644 index 0000000..e09a22b --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/darkcore/core_types/path_linux.go @@ -0,0 +1,5 @@ +//go:build !windows + +package core_types + +const PATH_SEPARATOR = "/" diff --git a/vendor/github.com/darklab8/fl-darkstat/darkcore/core_types/path_windows.go b/vendor/github.com/darklab8/fl-darkstat/darkcore/core_types/path_windows.go new file mode 100644 index 0000000..51711d9 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/darkcore/core_types/path_windows.go @@ -0,0 +1,5 @@ +//go:build windows + +package core_types + +const PATH_SEPARATOR = "\\" diff --git a/vendor/github.com/darklab8/fl-darkstat/darkcore/core_types/types.go b/vendor/github.com/darklab8/fl-darkstat/darkcore/core_types/types.go new file mode 100644 index 0000000..a972f3d --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/darkcore/core_types/types.go @@ -0,0 +1,34 @@ +package core_types + +import ( + "context" +) + +type CtxKey string + +const GlobalParamsCtxKey CtxKey = "global_params" + +type Url string + +type StaticFileKind int64 + +const ( + StaticFileUnknown StaticFileKind = iota // default unkonwn + StaticFileJS + StaticFileIco + StaticFileCSS +) + +type StaticFile struct { + Content string + Filename string + Kind StaticFileKind +} + +type GlobalParamsI interface { + GetStaticRoot() string +} + +func GetCtx(ctx context.Context) GlobalParamsI { + return ctx.Value(GlobalParamsCtxKey).(GlobalParamsI) +} diff --git a/vendor/github.com/darklab8/fl-darkstat/darkcore/settings/logus/logus.go b/vendor/github.com/darklab8/fl-darkstat/darkcore/settings/logus/logus.go new file mode 100644 index 0000000..8b97d70 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/darkcore/settings/logus/logus.go @@ -0,0 +1,10 @@ +package logus + +import ( + _ "github.com/darklab8/fl-darkstat/darkcore/settings" + "github.com/darklab8/go-typelog/typelog" +) + +var Log *typelog.Logger = typelog.NewLogger("darkcore", + typelog.WithLogLevel(typelog.LEVEL_INFO), +) diff --git a/vendor/github.com/darklab8/fl-darkstat/darkcore/settings/settings.go b/vendor/github.com/darklab8/fl-darkstat/darkcore/settings/settings.go new file mode 100644 index 0000000..f110bfd --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/darkcore/settings/settings.go @@ -0,0 +1,32 @@ +package settings + +import ( + "fmt" + + "github.com/darklab8/go-utils/utils/enverant" + "github.com/darklab8/go-utils/utils/utils_settings" +) + +type DarkcoreEnvVars struct { + utils_settings.UtilsEnvs + Password string + Secret string + ExtraCookieHost string +} + +var Env DarkcoreEnvVars + +func GetEnvs(envs *enverant.Enverant) DarkcoreEnvVars { + Env = DarkcoreEnvVars{ + UtilsEnvs: utils_settings.GetEnvs(envs), + Password: envs.GetStrOr("DARKCORE_PASSWORD", ""), + Secret: envs.GetStrOr("DARKCORE_SECRET", "passphrasewhichneedstobe32bytes!"), + } + return Env +} + +func init() { + env := enverant.NewEnverant() + Env = GetEnvs(env) + fmt.Sprintln("conf=", Env) +} diff --git a/vendor/github.com/darklab8/fl-darkstat/darkrpc/info.go b/vendor/github.com/darklab8/fl-darkstat/darkrpc/info.go new file mode 100644 index 0000000..ba49ccb --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/darkrpc/info.go @@ -0,0 +1,353 @@ +package darkrpc + +import ( + "encoding/json" + "fmt" + "sort" + "strings" + + "github.com/darklab8/fl-darkstat/darkstat/configs_export" + "github.com/darklab8/fl-darkstat/darkstat/front/tab" + "github.com/darklab8/fl-darkstat/darkstat/settings/logus" + "gopkg.in/yaml.v3" +) + +type GetInfoArgs struct { + Query string +} + +type GetInfoReply struct { + Content []string + Found []InfoFound +} + +func (t *ServerRpc) IsInfoFound(args GetInfoArgs, name string, nickname string) (bool, bool) { + lowered_query := strings.ToLower(args.Query) + if strings.Contains(strings.ToLower(name), lowered_query) { + return true, false + } + + if strings.Contains(strings.ToLower(nickname), lowered_query) { + if _, ok := t.app_data.Configs.Hashes[nickname]; ok && nickname == lowered_query { + return true, true // perfect nickname match + } + + return true, false + } + + first_line_in_infocard := tab.GetFirstLine(t.app_data.Configs.Infocards, configs_export.InfocardKey(nickname)) + if strings.Contains(strings.ToLower(first_line_in_infocard), lowered_query) { + return true, false + } + + return false, false +} + +func EntityToYamlStrings(entity any) []string { + var Content []string + data, err := json.Marshal(entity) + if err != nil { + Content = append(Content, err.Error()) + } + var hashmap map[string]interface{} + err = json.Unmarshal(data, &hashmap) + delete(hashmap, "BGCS_base_run_by") + delete(hashmap, "file") + delete(hashmap, "li01_01_base") + delete(hashmap, "InfocardKey") + delete(hashmap, "rephacks") + delete(hashmap, "equipment_slots") + delete(hashmap, "biggest_hardpoint") + delete(hashmap, "ship_packages") + delete(hashmap, "reputations") + delete(hashmap, "bribe") + delete(hashmap, "archetypes") + delete(hashmap, "damage_bonuses") + for key, _ := range hashmap { + if strings.Contains(key, "_hash") { + delete(hashmap, key) + } + } + if err != nil { + Content = append(Content, err.Error()) + } + yaml_bytes, err := yaml.Marshal(hashmap) + if err != nil { + Content = append(Content, err.Error()) + } + yaml_strs := strings.Split(string(yaml_bytes), "\n") + Content = append(Content, "```yml") + Content = append(Content, yaml_strs...) + Content = append(Content, "```") + return Content +} + +type InfoFound struct { + Nickname string + Name string + Entity string + FirstLine string + Obtainable bool +} + +func (t *ServerRpc) NewInfoFound(Nickname string, Name string, Entity string, Obtainable bool) InfoFound { + return InfoFound{ + Name: Name, + Nickname: string(Nickname), + FirstLine: tab.GetFirstLine(t.app_data.Configs.Infocards, configs_export.InfocardKey(string(Nickname))), + Obtainable: Obtainable, + Entity: Entity, + } +} + +func (t *ServerRpc) GetInfo(args GetInfoArgs, reply *GetInfoReply) error { + + if strings.ReplaceAll(args.Query, " ", "") == "" { + reply.Content = []string{} + reply.Content = append(reply.Content, "Input some name (or nickname) parts of a Freelancer item, base or pob") + reply.Content = append(reply.Content, "for example: . info iw04_01_base") + + return nil + } + + set_infocard := func(nickname string) { + infocard := t.app_data.Configs.Infocards[configs_export.InfocardKey(nickname)] + for _, line := range infocard { + reply.Content = append(reply.Content, line.ToStr()) + } + } + + for _, item := range t.app_data.Configs.Bases { + if ok, is_perfect_nickname_match := t.IsInfoFound(args, item.Name, string(item.Nickname)); ok { + reply.Content = []string{"entity: **Base**"} + reply.Found = append(reply.Found, t.NewInfoFound(string(item.Nickname), item.Name, "Base", false)) + reply.Content = append(reply.Content, EntityToYamlStrings(item)...) + set_infocard(string(item.Nickname)) + if is_perfect_nickname_match { + return nil + } + } + } + for _, item := range t.app_data.Configs.Ammos { + if ok, is_perfect_nickname_match := t.IsInfoFound(args, item.Name, string(item.Nickname)); ok { + reply.Content = []string{"entity: **Ammo**"} + reply.Found = append(reply.Found, t.NewInfoFound(string(item.Nickname), item.Name, "Ammo", t.app_data.Configs.Buyable(item.Bases))) + reply.Content = append(reply.Content, EntityToYamlStrings(item)...) + set_infocard(string(item.Nickname)) + if is_perfect_nickname_match { + return nil + } + } + } + for _, item := range t.app_data.Configs.MiningOperations { + if ok, is_perfect_nickname_match := t.IsInfoFound(args, item.Name, string(item.Nickname)); ok { + reply.Content = []string{"entity: **Mining Operation**"} + reply.Found = append(reply.Found, t.NewInfoFound(string(item.Nickname), item.Name, "Mining Operation", false)) + reply.Content = append(reply.Content, EntityToYamlStrings(item)...) + set_infocard(string(item.Nickname)) + if is_perfect_nickname_match { + return nil + } + } + } + for _, item := range t.app_data.Configs.Factions { + if ok, is_perfect_nickname_match := t.IsInfoFound(args, item.Name, string(item.Nickname)); ok { + reply.Content = []string{"entity: **Faction**"} + reply.Found = append(reply.Found, t.NewInfoFound(string(item.Nickname), item.Name, "Faction", false)) + reply.Content = append(reply.Content, EntityToYamlStrings(item)...) + set_infocard(string(item.Nickname)) + if is_perfect_nickname_match { + return nil + } + } + } + for _, item := range t.app_data.Configs.Commodities { + if ok, is_perfect_nickname_match := t.IsInfoFound(args, item.Name, string(item.Nickname)); ok { + reply.Content = []string{"entity: **Commodity**"} + reply.Found = append(reply.Found, t.NewInfoFound(string(item.Nickname), item.Name, "Commodity", t.app_data.Configs.Buyable(item.Bases))) + reply.Content = append(reply.Content, EntityToYamlStrings(item)...) + set_infocard(string(item.Nickname)) + if is_perfect_nickname_match { + return nil + } + } + } + for _, item := range t.app_data.Configs.Guns { + if ok, is_perfect_nickname_match := t.IsInfoFound(args, item.Name, string(item.Nickname)); ok { + reply.Content = []string{"entity: **Gun**"} + reply.Found = append(reply.Found, t.NewInfoFound(string(item.Nickname), item.Name, "Gun", t.app_data.Configs.Buyable(item.Bases))) + reply.Content = append(reply.Content, EntityToYamlStrings(item)...) + set_infocard(string(item.Nickname)) + if is_perfect_nickname_match { + return nil + } + } + } + for _, item := range t.app_data.Configs.Missiles { + if ok, is_perfect_nickname_match := t.IsInfoFound(args, item.Name, string(item.Nickname)); ok { + reply.Content = []string{"entity: **Missile**"} + reply.Found = append(reply.Found, t.NewInfoFound(string(item.Nickname), item.Name, "Missile", t.app_data.Configs.Buyable(item.Bases))) + reply.Content = append(reply.Content, EntityToYamlStrings(item)...) + set_infocard(string(item.Nickname)) + if is_perfect_nickname_match { + return nil + } + } + } + for _, item := range t.app_data.Configs.Mines { + if ok, is_perfect_nickname_match := t.IsInfoFound(args, item.Name, string(item.Nickname)); ok { + reply.Content = []string{"entity: **Mine**"} + reply.Found = append(reply.Found, t.NewInfoFound(string(item.Nickname), item.Name, "Mine", t.app_data.Configs.Buyable(item.Bases))) + reply.Content = append(reply.Content, EntityToYamlStrings(item)...) + set_infocard(string(item.Nickname)) + if is_perfect_nickname_match { + return nil + } + } + } + for _, item := range t.app_data.Configs.Shields { + if ok, is_perfect_nickname_match := t.IsInfoFound(args, item.Name, string(item.Nickname)); ok { + reply.Content = []string{"entity: **Shield**"} + reply.Found = append(reply.Found, t.NewInfoFound(string(item.Nickname), item.Name, "Shield", t.app_data.Configs.Buyable(item.Bases))) + reply.Content = append(reply.Content, EntityToYamlStrings(item)...) + set_infocard(string(item.Nickname)) + if is_perfect_nickname_match { + return nil + } + } + } + for _, item := range t.app_data.Configs.Ships { + if ok, is_perfect_nickname_match := t.IsInfoFound(args, item.Name, string(item.Nickname)); ok { + reply.Content = []string{"entity: **Ship**"} + reply.Found = append(reply.Found, t.NewInfoFound(string(item.Nickname), item.Name, "Ship", t.app_data.Configs.Buyable(item.Bases))) + reply.Content = append(reply.Content, EntityToYamlStrings(item)...) + set_infocard(string(item.Nickname)) + if is_perfect_nickname_match { + return nil + } + } + } + for _, item := range t.app_data.Configs.Thrusters { + if ok, is_perfect_nickname_match := t.IsInfoFound(args, item.Name, string(item.Nickname)); ok { + reply.Content = []string{"entity: **Thruster**"} + reply.Found = append(reply.Found, t.NewInfoFound(string(item.Nickname), item.Name, "Thruster", t.app_data.Configs.Buyable(item.Bases))) + reply.Content = append(reply.Content, EntityToYamlStrings(item)...) + set_infocard(string(item.Nickname)) + if is_perfect_nickname_match { + return nil + } + } + } + for _, item := range t.app_data.Configs.Tractors { + if ok, is_perfect_nickname_match := t.IsInfoFound(args, item.Name, string(item.Nickname)); ok { + reply.Content = []string{"entity: **Tractor**"} + reply.Found = append(reply.Found, t.NewInfoFound(string(item.Nickname), item.Name, "Tractor", t.app_data.Configs.Buyable(item.Bases))) + reply.Content = append(reply.Content, EntityToYamlStrings(item)...) + set_infocard(string(item.Nickname)) + if is_perfect_nickname_match { + return nil + } + } + } + for _, item := range t.app_data.Configs.Engines { + if ok, is_perfect_nickname_match := t.IsInfoFound(args, item.Name, string(item.Nickname)); ok { + reply.Content = []string{"entity: **Engine**"} + reply.Found = append(reply.Found, t.NewInfoFound(string(item.Nickname), item.Name, "Engine", t.app_data.Configs.Buyable(item.Bases))) + reply.Content = append(reply.Content, EntityToYamlStrings(item)...) + set_infocard(string(item.Nickname)) + if is_perfect_nickname_match { + return nil + } + } + } + for _, item := range t.app_data.Configs.Cloaks { + if ok, is_perfect_nickname_match := t.IsInfoFound(args, item.Name, string(item.Nickname)); ok { + reply.Content = []string{"entity: **CloakingDevice**"} + reply.Found = append(reply.Found, t.NewInfoFound(string(item.Nickname), item.Name, "Cloak", t.app_data.Configs.Buyable(item.Bases))) + reply.Content = append(reply.Content, EntityToYamlStrings(item)...) + set_infocard(string(item.Nickname)) + if is_perfect_nickname_match { + return nil + } + } + } + for _, item := range t.app_data.Configs.CMs { + if ok, is_perfect_nickname_match := t.IsInfoFound(args, item.Name, string(item.Nickname)); ok { + reply.Content = []string{"entity: **Counter Measure**"} + reply.Found = append(reply.Found, t.NewInfoFound(string(item.Nickname), item.Name, "CM", t.app_data.Configs.Buyable(item.Bases))) + reply.Content = append(reply.Content, EntityToYamlStrings(item)...) + set_infocard(string(item.Nickname)) + if is_perfect_nickname_match { + return nil + } + } + } + for _, item := range t.app_data.Configs.Scanners { + if ok, is_perfect_nickname_match := t.IsInfoFound(args, item.Name, string(item.Nickname)); ok { + reply.Content = []string{"entity: **Scanner**"} + reply.Found = append(reply.Found, t.NewInfoFound(string(item.Nickname), item.Name, "Scanner", t.app_data.Configs.Buyable(item.Bases))) + reply.Content = append(reply.Content, EntityToYamlStrings(item)...) + set_infocard(string(item.Nickname)) + if is_perfect_nickname_match { + return nil + } + } + } + for _, item := range t.app_data.Configs.PoBs { + if ok, is_perfect_nickname_match := t.IsInfoFound(args, item.Name, string(item.Nickname)); ok { + reply.Content = []string{"entity: **Player Owned Base**"} + reply.Found = append(reply.Found, t.NewInfoFound(string(item.Nickname), item.Name, "PoB", false)) + reply.Content = append(reply.Content, EntityToYamlStrings(item.PoBWithoutGoods)...) + set_infocard(string(item.Nickname)) + if is_perfect_nickname_match { + return nil + } + } + } + + if len(reply.Found) == 0 { + reply.Content = []string{} + reply.Content = append(reply.Content, "no matching names or nicknames of entities were found.") + } + if len(reply.Found) > 1 { + reply.Content = []string{} + var sb strings.Builder + + sort.Slice(reply.Found, func(i, j int) bool { + if reply.Found[i].Obtainable != reply.Found[j].Obtainable { + return reply.Found[i].Obtainable + } + return reply.Found[i].Name < reply.Found[j].Name + }) + + sb.WriteString("Multiple entities were found possessing same name and nickname. ") + sb.WriteString(fmt.Sprintf("Repeat request with more precise **name** or **nickname**. Printing no more than 10 matched entities (total matched: %d):", len(reply.Found))) + reply.Content = append(reply.Content, sb.String()) + for i := 0; i < 10 && i < len(reply.Found); i++ { + var sb strings.Builder + sb.WriteString(fmt.Sprintf("- **Name**: %s, **Nickname**: %s, **Type**: %s", reply.Found[i].Name, reply.Found[i].Nickname, reply.Found[i].Entity)) + if reply.Found[i].Obtainable { + sb.WriteString(", **Obtainable**: true") + } + sb.WriteString(fmt.Sprintf(", **InfoName**: %s", reply.Found[i].FirstLine)) + reply.Content = append(reply.Content, sb.String()) + } + } + + return nil +} + +func (r *ClientRpc) GetInfo(args GetInfoArgs, reply *GetInfoReply) error { + // Synchronous call + // return r.client.Call("ServerRpc.GetBases", args, &reply) + + // // Asynchronous call + client, err := r.getClient() + if logus.Log.CheckWarn(err, "dialing:") { + return err + } + + divCall := client.Go("ServerRpc.GetInfo", args, &reply, nil) + replyCall := <-divCall.Done // will be equal to divCall + return replyCall.Error +} diff --git a/vendor/github.com/darklab8/fl-darkstat/darkrpc/rpc.go b/vendor/github.com/darklab8/fl-darkstat/darkrpc/rpc.go new file mode 100644 index 0000000..95187c0 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/darkrpc/rpc.go @@ -0,0 +1,104 @@ +package darkrpc + +import ( + "net/rpc" + + "github.com/darklab8/fl-darkstat/darkstat/appdata" + "github.com/darklab8/fl-darkstat/darkstat/configs_export" + "github.com/darklab8/fl-darkstat/darkstat/settings/logus" +) + +type ServerRpc struct { + app_data *appdata.AppData +} + +func NewRpc(app_data *appdata.AppData) RpcI { + return &ServerRpc{app_data: app_data} +} + +type Args struct { +} +type Reply struct { + Bases []*configs_export.Base +} + +// / CLIENT/////////////////// +type ClientRpc struct { + sock string +} + +const DarkstatRpcSock = "/tmp/darkstat/rpc.sock" + +type ClientOpt func(r *ClientRpc) + +func WithSockCli(sock string) ClientOpt { + return func(r *ClientRpc) { + r.sock = sock + } +} + +func NewClient(opts ...ClientOpt) RpcI { + cli := &ClientRpc{} + + for _, opt := range opts { + opt(cli) + } + + return RpcI(cli) +} + +func (r *ClientRpc) getClient() (*rpc.Client, error) { + // client, err := rpc.DialHTTP("tcp", "127.0.0.1+":1234") // if serving over http + client, err := rpc.Dial("unix", r.sock) // if connecting over cli over sock + + if logus.Log.CheckWarn(err, "dialing:") { + return nil, err + } + + return client, err +} + +//// Methods + +type RpcI interface { + GetBases(args Args, reply *Reply) error + GetHealth(args Args, reply *bool) error + GetInfo(args GetInfoArgs, reply *GetInfoReply) error +} + +func (t *ServerRpc) GetBases(args Args, reply *Reply) error { + reply.Bases = t.app_data.Configs.Bases + return nil +} + +func (r *ClientRpc) GetBases(args Args, reply *Reply) error { + // Synchronous call + // return r.client.Call("ServerRpc.GetBases", args, &reply) + + // // Asynchronous call + client, err := r.getClient() + if logus.Log.CheckWarn(err, "dialing:") { + return err + } + + divCall := client.Go("ServerRpc.GetBases", args, &reply, nil) + replyCall := <-divCall.Done // will be equal to divCall + return replyCall.Error +} + +func (t *ServerRpc) GetHealth(args Args, reply *bool) error { + *reply = true + logus.Log.Info("rpc server got health checked") + return nil +} + +func (r *ClientRpc) GetHealth(args Args, reply *bool) error { + client, err := r.getClient() + if logus.Log.CheckWarn(err, "dialing:") { + return err + } + + divCall := client.Go("ServerRpc.GetHealth", args, &reply, nil) + replyCall := <-divCall.Done // will be equal to divCall + return replyCall.Error +} diff --git a/vendor/github.com/darklab8/fl-darkstat/darkrpc/server.go b/vendor/github.com/darklab8/fl-darkstat/darkrpc/server.go new file mode 100644 index 0000000..de85c70 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/darkrpc/server.go @@ -0,0 +1,84 @@ +package darkrpc + +import ( + "fmt" + "log" + "net" + "net/http" + "net/rpc" + "os" + + "github.com/darklab8/fl-darkstat/configs/cfg" + "github.com/darklab8/fl-darkstat/darkstat/appdata" + "github.com/darklab8/fl-darkstat/darkstat/settings/logus" +) + +type RpcServer struct { + sock_address string + port int +} + +func NewRpcServer(opts ...ServerOpt) *RpcServer { + srv := &RpcServer{ + port: 8100, + } + for _, opt := range opts { + opt(srv) + } + return srv +} + +type ServerOpt func(r *RpcServer) + +func WithSockSrv(sock string) ServerOpt { + return func(r *RpcServer) { + r.sock_address = sock + } +} + +func WithPortSrv(port int) ServerOpt { + return func(r *RpcServer) { + r.port = port + } +} + +func (r *RpcServer) Serve(app_data *appdata.AppData) { + rpcServer := rpc.NewServer() + rpc_server := NewRpc(app_data) + rpcServer.Register(rpc_server) + + rpc.HandleHTTP() // if serving over http + tcp_listener, err := net.Listen("tcp", fmt.Sprintf(":%d", r.port)) // if serving over http + if err != nil { + log.Fatal("listen error:", err) + } + + var sock_listener net.Listener + if cfg.IsLinux { + os.Remove(r.sock_address) + os.Mkdir("/tmp/darkstat", 0777) + sock_listener, err = net.Listen("unix", r.sock_address) // if serving over Unix + if err != nil { + log.Fatal("listen error:", err) + } + fmt.Println("turning on server") + if cfg.IsLinux { + go rpcServer.Accept(sock_listener) // if serving over Unix + + } + } + + go func() { + err := http.Serve(tcp_listener, nil) // if serving over Http + if err != nil { + log.Fatal("http error:", err) + } + }() + + fmt.Println("rpc server is launched") +} + +func (r *RpcServer) Close() { + fmt.Println("gracefully existing rpc server") + logus.Log.CheckError(os.Remove(r.sock_address), "unable removing sock") +} diff --git a/vendor/github.com/darklab8/fl-darkstat/darkstat/appdata/appdata.go b/vendor/github.com/darklab8/fl-darkstat/darkstat/appdata/appdata.go new file mode 100644 index 0000000..47af420 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/darkstat/appdata/appdata.go @@ -0,0 +1,151 @@ +package appdata + +import ( + "fmt" + "sync" + "time" + + "github.com/darklab8/fl-darkstat/configs/configs_mapped" + "github.com/darklab8/fl-darkstat/darkcore/builder" + "github.com/darklab8/fl-darkstat/darkcore/core_static" + "github.com/darklab8/fl-darkstat/darkstat/configs_export" + "github.com/darklab8/fl-darkstat/darkstat/front/static" + "github.com/darklab8/fl-darkstat/darkstat/front/static_front" + "github.com/darklab8/fl-darkstat/darkstat/front/types" + "github.com/darklab8/fl-darkstat/darkstat/front/urls" + "github.com/darklab8/fl-darkstat/darkstat/settings" + "github.com/darklab8/fl-darkstat/darkstat/settings/logus" + "github.com/darklab8/go-utils/utils/timeit" + "github.com/darklab8/go-utils/utils/utils_logus" +) + +type AppData struct { + Mapped *configs_mapped.MappedConfigs + Build *builder.Builder + Configs *configs_export.Exporter + Shared *types.SharedData + + mu sync.Mutex +} + +func (a *AppData) Lock() { a.mu.Lock() } +func (a *AppData) Unlock() { a.mu.Unlock() } + +func NewBuilder(mapped *configs_mapped.MappedConfigs) *builder.Builder { + var build *builder.Builder + timer_building_creation := timeit.NewTimer("building creation") + + tractor_tab_name := settings.Env.TractorTabName + if mapped.Discovery != nil { + tractor_tab_name = "IDs" + } + staticPrefix := "static/" + siteRoot := settings.Env.SiteRoot + params := &types.GlobalParams{ + Buildpath: "", + Theme: types.ThemeLight, + Themes: []string{ + siteRoot + urls.Index.ToString(), + siteRoot + urls.DarkIndex.ToString(), + siteRoot + urls.VanillaIndex.ToString(), + }, + TractorTabName: tractor_tab_name, + SiteUrl: settings.Env.SiteUrl, + SiteRoot: siteRoot, + StaticRoot: siteRoot + staticPrefix, + Heading: settings.Env.AppHeading, + Timestamp: time.Now().UTC(), + + RelayHost: settings.Env.RelayHost, + RelayRoot: settings.Env.RelayRoot, + } + + static_files := []builder.StaticFile{ + builder.NewStaticFileFromCore(core_static.HtmxJS), + builder.NewStaticFileFromCore(core_static.HtmxPreloadJS), + builder.NewStaticFileFromCore(core_static.SortableJS), + builder.NewStaticFileFromCore(core_static.ResetCSS), + builder.NewStaticFileFromCore(core_static.FaviconIco), + + builder.NewStaticFileFromCore(static_front.CommonCSS), + builder.NewStaticFileFromCore(static_front.CustomCSS), + builder.NewStaticFileFromCore(static_front.CustomJS), + builder.NewStaticFileFromCore(static_front.CustomJSResizer), + builder.NewStaticFileFromCore(static_front.CustomJSFiltering), + builder.NewStaticFileFromCore(static_front.CustomJSFilteringRoutes), + builder.NewStaticFileFromCore(static_front.CustomJSShared), + builder.NewStaticFileFromCore(static_front.CustomJSSharedDiscovery), + builder.NewStaticFileFromCore(static_front.CustomJSSharedVanilla), + } + + for _, file := range static.StaticFilesystem.Files { + static_files = append(static_files, builder.NewStaticFileFromCore(file)) + } + + build = builder.NewBuilder(params, static_files) + timer_building_creation.Close() + return build +} + +func NewMapped() *configs_mapped.MappedConfigs { + var mapped *configs_mapped.MappedConfigs + freelancer_folder := settings.Env.FreelancerFolder + + timeit.NewTimerF(func() { + mapped = configs_mapped.NewMappedConfigs() + }, timeit.WithMsg("MappedConfigs creation")) + logus.Log.Debug("scanning freelancer folder", utils_logus.FilePath(freelancer_folder)) + mapped.Read(freelancer_folder) + return mapped +} + +func NewAppData() *AppData { + mapped := NewMapped() + configs := configs_export.NewExporter(mapped) + build := NewBuilder(mapped) + + var data *configs_export.Exporter + timeit.NewTimerMF("exporting data", func() { data = configs.Export(configs_export.ExportOptions{}) }) + + var shared *types.SharedData = &types.SharedData{ + Mapped: mapped, + } + + timeit.NewTimerMF("filtering to useful stuff", func() { + if mapped.FLSR != nil { + shared.FLSRData = types.FLSRData{ + ShowFLSR: true, + } + } + + if mapped.Discovery != nil { + shared.DiscoveryData = types.DiscoveryData{ + ShowDisco: true, + Ids: configs.Tractors, + TractorsByID: configs.TractorsByID, + Config: mapped.Discovery.Techcompat, + LatestPatch: mapped.Discovery.LatestPatch, + OrderedTechcompat: *configs_export.NewOrderedTechCompat(configs), + } + } + fmt.Println("attempting to access l.configs.Infocards") + shared.Infocards = configs.Infocards + }) + + shared.CraftableBaseName = mapped.CraftableBaseName() + + return &AppData{ + Build: build, + Configs: data, + Shared: shared, + Mapped: mapped, + } +} + +func (a *AppData) Refresh() { + updated := NewAppData() + a.Build = updated.Build + a.Mapped = updated.Mapped + a.Shared = updated.Shared + a.Configs = updated.Configs +} diff --git a/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/ammo.go b/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/ammo.go new file mode 100644 index 0000000..7fc6c00 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/ammo.go @@ -0,0 +1,106 @@ +package configs_export + +import ( + "github.com/darklab8/fl-darkstat/configs/cfg" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/initialworld/flhash" + "github.com/darklab8/go-utils/utils/ptr" +) + +type Ammo struct { + Name string `json:"name"` + Price int `json:"price"` + + HitPts int `json:"hit_pts"` + Volume float64 `json:"volume"` + MunitionLifetime float64 `json:"munition_lifetime"` + + Nickname string `json:"nickname"` + NicknameHash flhash.HashCode `json:"nickname_hash" format:"int64"` + NameID int `json:"name_id"` + InfoID int `json:"info_id"` + SeekerType string `json:"seeker_type"` + SeekerRange int `json:"seeker_range"` + SeekerFovDeg int `json:"seeker_fov_deg"` + + Bases map[cfg.BaseUniNick]*MarketGood `json:"-" swaggerignore:"true"` + + *DiscoveryTechCompat `json:"-" swaggerignore:"true"` + + AmmoLimit AmmoLimit `json:"ammo_limit"` + Mass float64 `json:"mass"` +} + +func (b Ammo) GetNickname() string { return string(b.Nickname) } + +func (b Ammo) GetBases() map[cfg.BaseUniNick]*MarketGood { return b.Bases } + +func (b Ammo) GetDiscoveryTechCompat() *DiscoveryTechCompat { return b.DiscoveryTechCompat } + +func (e *Exporter) GetAmmo(ids []*Tractor) []Ammo { + var tractors []Ammo + + for _, munition_info := range e.Configs.Equip.Munitions { + munition := Ammo{ + Bases: make(map[cfg.BaseUniNick]*MarketGood), + } + munition.Mass, _ = munition_info.Mass.GetValue() + + munition.Nickname = munition_info.Nickname.Get() + munition.NicknameHash = flhash.HashNickname(munition.Nickname) + e.Hashes[munition.Nickname] = munition.NicknameHash + munition.NameID, _ = munition_info.IdsName.GetValue() + munition.InfoID, _ = munition_info.IdsInfo.GetValue() + + munition.HitPts, _ = munition_info.HitPts.GetValue() + + if value, ok := munition_info.AmmoLimitAmountInCatridge.GetValue(); ok { + munition.AmmoLimit.AmountInCatridge = ptr.Ptr(value) + } + if value, ok := munition_info.AmmoLimitMaxCatridges.GetValue(); ok { + munition.AmmoLimit.MaxCatridges = ptr.Ptr(value) + } + + munition.Volume, _ = munition_info.Volume.GetValue() + munition.SeekerRange, _ = munition_info.SeekerRange.GetValue() + munition.SeekerType, _ = munition_info.SeekerType.GetValue() + + munition.MunitionLifetime, _ = munition_info.LifeTime.GetValue() + + munition.SeekerFovDeg, _ = munition_info.SeekerFovDeg.GetValue() + + if ammo_ids_name, ok := munition_info.IdsName.GetValue(); ok { + munition.Name = e.GetInfocardName(ammo_ids_name, munition.Nickname) + } + + munition.Price = -1 + if good_info, ok := e.Configs.Goods.GoodsMap[munition_info.Nickname.Get()]; ok { + if price, ok := good_info.Price.GetValue(); ok { + munition.Price = price + munition.Bases = e.GetAtBasesSold(GetCommodityAtBasesInput{ + Nickname: good_info.Nickname.Get(), + Price: price, + }) + } + } + + if !e.Buyable(munition.Bases) { + continue + } + + e.exportInfocards(InfocardKey(munition.Nickname), munition.InfoID) + munition.DiscoveryTechCompat = CalculateTechCompat(e.Configs.Discovery, ids, munition.Nickname) + tractors = append(tractors, munition) + } + return tractors +} + +func (e *Exporter) FilterToUsefulAmmo(cms []Ammo) []Ammo { + var useful_items []Ammo = make([]Ammo, 0, len(cms)) + for _, item := range cms { + if !e.Buyable(item.Bases) { + continue + } + useful_items = append(useful_items, item) + } + return useful_items +} diff --git a/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/bases.go b/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/bases.go new file mode 100644 index 0000000..88fb059 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/bases.go @@ -0,0 +1,224 @@ +package configs_export + +import ( + "fmt" + "strings" + + "github.com/darklab8/fl-darkstat/configs/cfg" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/initialworld/flhash" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/universe_mapped" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/universe_mapped/systems_mapped" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/infocard_mapped/infocard" + "github.com/darklab8/go-utils/utils/utils_types" +) + +func (e *Exporter) TraderExists(base_nickname string) bool { + universe_base, ok := e.Configs.Universe.BasesMap[universe_mapped.BaseNickname(base_nickname)] + if !ok { + return true + } + return universe_base.TraderExists +} + +func VectorToSectorCoord(system *universe_mapped.System, pos cfg.Vector) string { + var scale float64 = 1.0 + if value, ok := system.NavMapScale.GetValue(); ok { + scale = value + } + + var fGridSize float64 = 34000.0 / scale // 34000 suspiciously looks like math.MaxInt16 + var gridRefX = int((pos.X+(fGridSize*5))/fGridSize) - 1 + var gridRefZ = int((pos.Z+(fGridSize*5))/fGridSize) - 1 + gridRefX = min(max(gridRefX, 0), 7) + scXPos := rune('A' + gridRefX) + gridRefZ = min(max(gridRefZ, 0), 7) + scZPos := rune('1' + gridRefZ) + return fmt.Sprintf("%c-%c", scXPos, scZPos) + +} + +func (e *Exporter) GetBases() []*Base { + results := make([]*Base, 0, len(e.Configs.Universe.Bases)) + + commodities_per_base := e.getMarketGoods() + + for _, base := range e.Configs.Universe.Bases { + var name string = e.GetInfocardName(base.StridName.Get(), base.Nickname.Get()) + + var system_name infocard.Infoname + var Region string + system, found_system := e.Configs.Universe.SystemMap[universe_mapped.SystemNickname(base.System.Get())] + + if found_system { + + system_name = infocard.Infoname(e.GetInfocardName(system.StridName.Get(), system.Nickname.Get())) + + Region = e.GetRegionName(system) + } + + var infocard_id int + var reputation_nickname string + var pos cfg.Vector + + var archetypes []string + + if system, ok := e.Configs.Systems.SystemsMap[base.System.Get()]; ok { + if system_base, ok := system.BasesByBases[base.Nickname.Get()]; ok { + infocard_id = system_base.IDsInfo.Get() + reputation_nickname = system_base.RepNickname.Get() + } + + if system_bases, ok := system.AllBasesByDockWith[base.Nickname.Get()]; ok { + for _, system_base := range system_bases { + pos, _ = system_base.Pos.GetValue() + archetype, _ := system_base.Archetype.GetValue() + archetypes = append(archetypes, archetype) + } + } + } + + var infocard_ids []int = make([]int, 0) + + infocard_ids = append(infocard_ids, infocard_id) + + if infocard_middle_id, exists := e.Configs.InfocardmapINI.InfocardMapTable.Map[infocard_id]; exists { + infocard_ids = append(infocard_ids, infocard_middle_id) + } + + var factionName string + if group, exists := e.Configs.InitialWorld.GroupsMap[reputation_nickname]; exists { + infocard_ids = append(infocard_ids, group.IdsInfo.Get()) + factionName = e.GetInfocardName(group.IdsName.Get(), reputation_nickname) + } + + var market_goods_per_good_nick map[CommodityKey]*MarketGood = make(map[CommodityKey]*MarketGood) + + if found_commodities, ok := commodities_per_base[cfg.BaseUniNick(base.Nickname.Get())]; ok { + market_goods_per_good_nick = found_commodities + } + + var nickname cfg.BaseUniNick = cfg.BaseUniNick(base.Nickname.Get()) + + e.exportInfocards(InfocardKey(nickname), infocard_ids...) + + base := &Base{ + Missions: &BaseMissions{}, + Name: name, + Nickname: nickname, + NicknameHash: flhash.HashNickname(nickname.ToStr()), + FactionName: factionName, + System: string(system_name), + SystemNickname: base.System.Get(), + SystemNicknameHash: flhash.HashNickname(base.System.Get()), + StridName: base.StridName.Get(), + InfocardID: infocard_id, + InfocardKey: InfocardKey(nickname), + File: utils_types.FilePath(base.File.Get()), + BGCS_base_run_by: base.BGCS_base_run_by.Get(), + MarketGoodsPerNick: market_goods_per_good_nick, + Pos: pos, + Archetypes: archetypes, + Region: Region, + } + + e.Hashes[string(base.Nickname)] = base.NicknameHash + e.Hashes[base.SystemNickname] = base.SystemNicknameHash + + if found_system { + base.SectorCoord = VectorToSectorCoord(system, base.Pos) + } + + results = append(results, base) + } + + return results +} + +func EnhanceBasesWithServerOverrides(bases []*Base, commodities []*Commodity) { + var base_per_nick map[cfg.BaseUniNick]*Base = make(map[cfg.BaseUniNick]*Base) + for _, base := range bases { + base_per_nick[base.Nickname] = base + } + + for _, commodity := range commodities { + for _, market_good := range commodity.Bases { + commodity_key := GetCommodityKey(market_good.Nickname, market_good.ShipClass) + if base, ok := base_per_nick[market_good.BaseNickname]; ok { + base.MarketGoodsPerNick[commodity_key] = market_good + } + } + } +} + +func FilterToUserfulBases(bases []*Base) []*Base { + var useful_bases []*Base = make([]*Base, 0, len(bases)) + for _, item := range bases { + if item.IsPob { + useful_bases = append(useful_bases, item) + continue + } + + if item.Reachable { + useful_bases = append(useful_bases, item) + continue + } + + if (item.Name == "Object Unknown" || item.Name == "") && len(item.MarketGoodsPerNick) == 0 { + continue + } + + if strings.Contains(item.System, "Bastille") { + continue + } + + is_invisible := true + for _, archetype := range item.Archetypes { + if archetype != systems_mapped.BaseArchetypeInvisible { + is_invisible = false + } + } + if is_invisible { + continue + } + useful_bases = append(useful_bases, item) + } + return useful_bases +} + +type Base struct { + Name string `json:"name"` // Infocard Name + Archetypes []string `json:"archetypes"` // Base Archetypes + Nickname cfg.BaseUniNick `json:"nickname"` + NicknameHash flhash.HashCode `json:"nickname_hash" format:"int64"` // Flhash of nickname + FactionName string `json:"faction_nickname"` + System string `json:"system_name"` + SystemNickname string `json:"system_nickname"` + SystemNicknameHash flhash.HashCode `json:"system_nickname_hash" format:"int64"` + Region string `json:"region_name"` + StridName int `json:"strid_name"` + InfocardID int `json:"infocard_id"` + InfocardKey InfocardKey + File utils_types.FilePath `json:"file"` + BGCS_base_run_by string + MarketGoodsPerNick map[CommodityKey]*MarketGood `json:"-" swaggerignore:"true"` + Pos cfg.Vector `json:"pos"` + SectorCoord string `json:"sector_coord"` + + IsTransportUnreachable bool `json:"is_transport_unreachable"` // Check if base is NOT reachable from manhattan by Transport through Graph method (at Discovery base has to have Transport dockable spheres) + + Missions *BaseMissions `json:"-" swaggerignore:"true"` + baseAllTradeRoutes `json:"-" swaggerignore:"true"` + baseAllRoutes `json:"-" swaggerignore:"true"` + *MiningInfo `json:"mining_info,omitempty"` + + Reachable bool `json:"is_reachhable"` // is base IS Rechable by frighter from Manhattan + IsPob bool +} + +func (b Base) GetNickname() string { return string(b.Nickname) } + +type CommodityKey string + +func GetCommodityKey(nickname string, ship_class cfg.ShipClass) CommodityKey { + return CommodityKey(fmt.Sprintf("%s_%d", nickname, ship_class)) +} diff --git a/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/cloaks.go b/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/cloaks.go new file mode 100644 index 0000000..7171aa4 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/cloaks.go @@ -0,0 +1,72 @@ +package configs_export + +import ( + "github.com/darklab8/fl-darkstat/configs/cfg" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/initialworld/flhash" +) + +type Cloak struct { + Name string `json:"name"` + Price int `json:"price"` + + HitPts int `json:"hit_pts"` + Volume float64 `json:"volume"` + + Nickname string `json:"nickname"` + NameID int `json:"name_id"` + InfoID int `json:"info_id"` + + PowerUsage float64 + CloakInTime int + CloakOutTime int + + Bases map[cfg.BaseUniNick]*MarketGood `json:"-" swaggerignore:"true"` + *DiscoveryTechCompat `json:"-" swaggerignore:"true"` +} + +func (b Cloak) GetNickname() string { return string(b.Nickname) } + +func (b Cloak) GetBases() map[cfg.BaseUniNick]*MarketGood { return b.Bases } + +func (b Cloak) GetDiscoveryTechCompat() *DiscoveryTechCompat { return b.DiscoveryTechCompat } + +func (e *Exporter) GetCloaks(ids []*Tractor) []Cloak { + var items []Cloak + + for _, cloak_info := range e.Configs.Equip.Cloaks { + cloak := Cloak{ + Bases: make(map[cfg.BaseUniNick]*MarketGood), + } + cloak.PowerUsage, _ = cloak_info.PowerUsage.GetValue() + cloak.CloakInTime, _ = cloak_info.CloakInTime.GetValue() + cloak.CloakOutTime, _ = cloak_info.CloakOutTime.GetValue() + cloak.Volume, _ = cloak_info.Volume.GetValue() + + cloak.Nickname = cloak_info.Nickname.Get() + NicknameHash := flhash.HashNickname(cloak.Nickname) + e.Hashes[cloak.Nickname] = NicknameHash + cloak.NameID, _ = cloak_info.IdsName.GetValue() + cloak.InfoID, _ = cloak_info.IdsInfo.GetValue() + cloak.HitPts, _ = cloak_info.HitPts.GetValue() + + if item_name, ok := cloak_info.IdsName.GetValue(); ok { + cloak.Name = e.GetInfocardName(item_name, cloak.Nickname) + } + + cloak.Price = -1 + if good_info, ok := e.Configs.Goods.GoodsMap[cloak_info.Nickname.Get()]; ok { + if price, ok := good_info.Price.GetValue(); ok { + cloak.Price = price + cloak.Bases = e.GetAtBasesSold(GetCommodityAtBasesInput{ + Nickname: good_info.Nickname.Get(), + Price: price, + }) + } + } + + e.exportInfocards(InfocardKey(cloak.Nickname), cloak.InfoID) + cloak.DiscoveryTechCompat = CalculateTechCompat(e.Configs.Discovery, ids, cloak.Nickname) + items = append(items, cloak) + } + return items +} diff --git a/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/cm.go b/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/cm.go new file mode 100644 index 0000000..84f174f --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/cm.go @@ -0,0 +1,104 @@ +package configs_export + +import ( + "github.com/darklab8/fl-darkstat/configs/cfg" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/initialworld/flhash" + "github.com/darklab8/go-utils/utils/ptr" +) + +type CounterMeasure struct { + Name string `json:"name"` + Price int `json:"price"` + + HitPts int `json:"hit_pts"` + AIRange int `json:"ai_range"` + Lifetime int `json:"lifetime"` + Range int `json:"range"` + DiversionPctg int `json:"diversion_pctg"` + + Lootable bool `json:"lootable"` + Nickname string `json:"nickname"` + NicknameHash flhash.HashCode `json:"nickname_hash" format:"int64"` + NameID int `json:"name_id"` + InfoID int `json:"indo_id"` + + Bases map[cfg.BaseUniNick]*MarketGood `json:"-" swaggerignore:"true"` + + *DiscoveryTechCompat `json:"-" swaggerignore:"true"` + + AmmoLimit AmmoLimit `json:"ammo_limit"` + Mass float64 `json:"mass"` +} + +func (b CounterMeasure) GetNickname() string { return string(b.Nickname) } + +func (b CounterMeasure) GetBases() map[cfg.BaseUniNick]*MarketGood { return b.Bases } + +func (b CounterMeasure) GetDiscoveryTechCompat() *DiscoveryTechCompat { return b.DiscoveryTechCompat } + +func (e *Exporter) GetCounterMeasures(ids []*Tractor) []CounterMeasure { + var tractors []CounterMeasure + + for _, cm_info := range e.Configs.Equip.CounterMeasureDroppers { + cm := CounterMeasure{ + Bases: make(map[cfg.BaseUniNick]*MarketGood), + } + cm.Mass, _ = cm_info.Mass.GetValue() + + cm.Nickname = cm_info.Nickname.Get() + cm.NicknameHash = flhash.HashNickname(cm.Nickname) + e.Hashes[cm.Nickname] = cm.NicknameHash + cm.HitPts = cm_info.HitPts.Get() + cm.AIRange = cm_info.AIRange.Get() + cm.Lootable = cm_info.Lootable.Get() + cm.NameID = cm_info.IdsName.Get() + cm.InfoID = cm_info.IdsInfo.Get() + + if good_info, ok := e.Configs.Goods.GoodsMap[cm.Nickname]; ok { + if price, ok := good_info.Price.GetValue(); ok { + cm.Price = price + cm.Bases = e.GetAtBasesSold(GetCommodityAtBasesInput{ + Nickname: good_info.Nickname.Get(), + Price: price, + }) + } + } + + cm.Name = e.GetInfocardName(cm.NameID, cm.Nickname) + + infocards := []int{cm.InfoID} + if ammo_info, ok := e.Configs.Equip.CounterMeasureMap[cm_info.ProjectileArchetype.Get()]; ok { + + if value, ok := ammo_info.AmmoLimitAmountInCatridge.GetValue(); ok { + cm.AmmoLimit.AmountInCatridge = ptr.Ptr(value) + } + if value, ok := ammo_info.AmmoLimitMaxCatridges.GetValue(); ok { + cm.AmmoLimit.MaxCatridges = ptr.Ptr(value) + } + + cm.Lifetime = ammo_info.Lifetime.Get() + cm.Range = ammo_info.Range.Get() + cm.DiversionPctg = ammo_info.DiversionPctg.Get() + + if id, ok := ammo_info.IdsInfo.GetValue(); ok { + infocards = append(infocards, id) + } + } + + e.exportInfocards(InfocardKey(cm.Nickname), infocards...) + cm.DiscoveryTechCompat = CalculateTechCompat(e.Configs.Discovery, ids, cm.Nickname) + tractors = append(tractors, cm) + } + return tractors +} + +func (e *Exporter) FilterToUsefulCounterMeasures(cms []CounterMeasure) []CounterMeasure { + var useful_items []CounterMeasure = make([]CounterMeasure, 0, len(cms)) + for _, item := range cms { + if !e.Buyable(item.Bases) { + continue + } + useful_items = append(useful_items, item) + } + return useful_items +} diff --git a/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/commodities.go b/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/commodities.go new file mode 100644 index 0000000..db04519 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/commodities.go @@ -0,0 +1,377 @@ +package configs_export + +import ( + "fmt" + + "github.com/darklab8/fl-darkstat/configs/cfg" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/initialworld/flhash" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/universe_mapped" + "github.com/darklab8/go-utils/utils/ptr" +) + +type MarketGood struct { + GoodInfo + + LevelRequired int `json:"level_required"` + RepRequired float64 `json:"rep_required"` + PriceBaseBuysFor *int `json:"price_base_buys_for"` + PriceBaseSellsFor int `json:"price_base_sells_for"` + Volume float64 `json:"volume"` + ShipClass cfg.ShipClass `json:"ship_class"` // Discovery specific value. Volume can be different based on ship class. Duplicating market goods with different volumes for that + BaseSells bool `json:"base_sells"` + IsServerSideOverride bool `json:"is_server_override"` + + NotBuyable bool `json:"_" swaggerignore:"true"` + IsTransportUnreachable bool `json:"_" swaggerignore:"true"` + + BaseInfo +} + +func (g MarketGood) GetPriceBaseBuysFor() int { + if g.PriceBaseBuysFor == nil { + return 0 + } + return *g.PriceBaseBuysFor +} + +type Commodity struct { + Nickname string `json:"nickname"` + NicknameHash flhash.HashCode `json:"nickname_hash" format:"int64"` + PriceBase int `json:"price_base"` + Name string `json:"name"` + Combinable bool `json:"combinable"` + Volume float64 `json:"volume"` + ShipClass cfg.ShipClass `json:"ship_class"` + NameID int `json:"name_id"` + InfocardID int `json:"infocard_id"` + Infocard InfocardKey `json:"infocard_key"` + Bases map[cfg.BaseUniNick]*MarketGood `json:"-" swaggerignore:"true"` + PriceBestBaseBuysFor int `json:"price_best_base_buys_for"` + PriceBestBaseSellsFor int `json:"price_best_base_sells_for"` + ProffitMargin int `json:"proffit_margin"` + baseAllTradeRoutes `json:"-" swaggerignore:"true"` + Mass float64 `json:"mass"` +} + +func (b Commodity) GetNickname() string { return string(b.Nickname) } + +func (b Commodity) GetBases() map[cfg.BaseUniNick]*MarketGood { return b.Bases } + +func GetPricePerVoume(price int, volume float64) float64 { + if volume == 0 { + return -1 + } + return float64(price) / float64(volume) +} + +func (e *Exporter) GetCommodities() []*Commodity { + commodities := make([]*Commodity, 0, 100) + + for _, comm := range e.Configs.Goods.Commodities { + equipment_name := comm.Equipment.Get() + equipment := e.Configs.Equip.CommoditiesMap[equipment_name] + + for _, volume_info := range equipment.Volumes { + commodity := &Commodity{ + Bases: make(map[cfg.BaseUniNick]*MarketGood), + PriceBase: comm.Price.Get(), + } + commodity.Mass, _ = equipment.Mass.GetValue() + + commodity.Nickname = comm.Nickname.Get() + commodity.NicknameHash = flhash.HashNickname(commodity.Nickname) + e.Hashes[commodity.Nickname] = commodity.NicknameHash + + commodity.Combinable = comm.Combinable.Get() + + commodity.NameID = equipment.IdsName.Get() + + commodity.Name = e.GetInfocardName(equipment.IdsName.Get(), commodity.Nickname) + e.exportInfocards(commodity.Infocard, equipment.IdsInfo.Get()) + commodity.InfocardID = equipment.IdsInfo.Get() + + commodity.Volume = volume_info.Volume.Get() + commodity.ShipClass = volume_info.GetShipClass() + commodity.Infocard = InfocardKey(commodity.Nickname) + + base_item_price := comm.Price.Get() + + commodity.Bases = e.GetAtBasesSold(GetCommodityAtBasesInput{ + Nickname: commodity.Nickname, + Price: base_item_price, + Volume: commodity.Volume, + ShipClass: commodity.ShipClass, + }) + + for _, base_info := range commodity.Bases { + if base_info.GetPriceBaseBuysFor() > commodity.PriceBestBaseBuysFor { + commodity.PriceBestBaseBuysFor = base_info.GetPriceBaseBuysFor() + } + if base_info.PriceBaseSellsFor < commodity.PriceBestBaseSellsFor || commodity.PriceBestBaseSellsFor == 0 { + if base_info.BaseSells && base_info.PriceBaseSellsFor > 0 { + commodity.PriceBestBaseSellsFor = base_info.PriceBaseSellsFor + } + + } + } + + if commodity.PriceBestBaseBuysFor > 0 && commodity.PriceBestBaseSellsFor > 0 { + commodity.ProffitMargin = commodity.PriceBestBaseBuysFor - commodity.PriceBestBaseSellsFor + } + + commodities = append(commodities, commodity) + } + + } + + return commodities +} + +type GetCommodityAtBasesInput struct { + Nickname string + Price int + Volume float64 + ShipClass cfg.ShipClass +} + +func (e *Exporter) ServerSideMarketGoodsOverrides(commodity GetCommodityAtBasesInput) map[cfg.BaseUniNick]*MarketGood { + var bases_already_found map[cfg.BaseUniNick]*MarketGood = make(map[cfg.BaseUniNick]*MarketGood) + + for _, base_market := range e.Configs.Discovery.Prices.BasesPerGood[commodity.Nickname] { + base_nickname := cfg.BaseUniNick(base_market.BaseNickname.Get()) + + defer func() { + if r := recover(); r != nil { + fmt.Println("Recovered in f", r) + fmt.Println("recovered base_nickname", base_nickname) + fmt.Println("recovered commodity nickname", commodity.Nickname) + panic(r) + } + }() + + var base_info *MarketGood = &MarketGood{ + GoodInfo: e.GetGoodInfo(commodity.Nickname), + BaseInfo: e.GetBaseInfo(universe_mapped.BaseNickname(base_nickname)), + NotBuyable: false, + BaseSells: base_market.BaseSells.Get(), + PriceBaseBuysFor: ptr.Ptr(base_market.PriceBaseBuysFor.Get()), + PriceBaseSellsFor: base_market.PriceBaseSellsFor.Get(), + Volume: commodity.Volume, + ShipClass: commodity.ShipClass, + IsServerSideOverride: true, + } + + if e.useful_bases_by_nick != nil { + if _, ok := e.useful_bases_by_nick[base_info.BaseNickname]; !ok { + base_info.NotBuyable = true + } + } + + bases_already_found[base_info.BaseNickname] = base_info + } + return bases_already_found +} + +func (e *Exporter) GetAtBasesSold(commodity GetCommodityAtBasesInput) map[cfg.BaseUniNick]*MarketGood { + var goods_per_base map[cfg.BaseUniNick]*MarketGood = make(map[cfg.BaseUniNick]*MarketGood) + + for _, base_market := range e.Configs.Market.BasesPerGood[commodity.Nickname] { + base_nickname := base_market.Base + + market_good := base_market.MarketGood + base_info := &MarketGood{ + GoodInfo: e.GetGoodInfo(commodity.Nickname), + BaseInfo: e.GetBaseInfo(universe_mapped.BaseNickname(base_nickname)), + NotBuyable: false, + Volume: commodity.Volume, + ShipClass: commodity.ShipClass, + } + base_info.BaseSells = market_good.BaseSells() + base_info.BaseNickname = base_nickname + + base_info.PriceBaseSellsFor = int(market_good.PriceModifier.Get() * float64(commodity.Price)) + + if e.Configs.Discovery != nil { + base_info.PriceBaseBuysFor = ptr.Ptr(market_good.BaseSellsIPositiveAndDiscoSellPrice.Get()) + } else { + base_info.PriceBaseBuysFor = ptr.Ptr(base_info.PriceBaseSellsFor) + } + + base_info.LevelRequired = market_good.LevelRequired.Get() + base_info.RepRequired = market_good.RepRequired.Get() + + base_info.BaseInfo = e.GetBaseInfo(universe_mapped.BaseNickname(base_info.BaseNickname)) + + if e.useful_bases_by_nick != nil { + if _, ok := e.useful_bases_by_nick[base_info.BaseNickname]; !ok { + base_info.NotBuyable = true + } + } + + goods_per_base[base_info.BaseNickname] = base_info + } + + if e.Configs.Discovery != nil { + serverside_overrides := e.ServerSideMarketGoodsOverrides(commodity) + for _, item := range serverside_overrides { + goods_per_base[item.BaseNickname] = item + } + + } + if e.Configs.Discovery != nil || e.Configs.FLSR != nil { + pob_produced := e.pob_produced() + if _, ok := pob_produced[commodity.Nickname]; ok { + good_to_add := &MarketGood{ + GoodInfo: e.GetGoodInfo(commodity.Nickname), + BaseSells: true, + IsServerSideOverride: true, + Volume: commodity.Volume, + ShipClass: commodity.ShipClass, + BaseInfo: BaseInfo{ + BaseNickname: pob_crafts_nickname, + BaseName: e.Configs.CraftableBaseName(), + SystemName: "Neverwhere", + Region: "Neverwhere", + FactionName: "Neverwhere", + }, + } + goods_per_base[pob_crafts_nickname] = good_to_add + + } + } + + loot_findable := e.findable_in_loot() + if _, ok := loot_findable[commodity.Nickname]; ok { + good_to_add := &MarketGood{ + GoodInfo: e.GetGoodInfo(commodity.Nickname), + BaseSells: true, + IsServerSideOverride: false, + Volume: commodity.Volume, + ShipClass: commodity.ShipClass, + BaseInfo: BaseInfo{ + BaseNickname: BaseLootableNickname, + BaseName: BaseLootableName, + SystemName: "Neverwhere", + Region: "Neverwhere", + FactionName: BaseLootableFaction, + }, + } + goods_per_base[BaseLootableNickname] = good_to_add + + } + + if e.Configs.Discovery != nil { + pob_buyable := e.get_pob_buyable() + if goods, ok := pob_buyable[commodity.Nickname]; ok { + for _, good := range goods { + good_to_add := &MarketGood{ + GoodInfo: e.GetGoodInfo(commodity.Nickname), + BaseSells: good.Quantity > good.MinStock, + IsServerSideOverride: true, + PriceBaseBuysFor: ptr.Ptr(good.SellPrice), + PriceBaseSellsFor: good.Price, + Volume: commodity.Volume, + ShipClass: commodity.ShipClass, + BaseInfo: BaseInfo{ + BaseNickname: cfg.BaseUniNick(good.PobNickname), + BaseName: "(PoB) " + good.PoBName, + SystemName: good.SystemName, + FactionName: good.FactionName, + }, + } + + if good.System != nil { + good_to_add.BaseInfo.Region = e.GetRegionName(good.System) + } + if good.BasePos != nil && good.System != nil { + good_to_add.BasePos = *good.BasePos + good_to_add.SectorCoord = VectorToSectorCoord(good.System, *good.BasePos) + } + goods_per_base[cfg.BaseUniNick(good.PobNickname)] = good_to_add + } + } + } + + for _, good := range goods_per_base { + good.GoodInfo = e.GetGoodInfo(commodity.Nickname) + } + + for base_nickname, good := range goods_per_base { + if !e.TraderExists(string(base_nickname)) { + if good.Category == "commodity" { + delete(goods_per_base, base_nickname) + } + } + } + + return goods_per_base +} + +type BaseInfo struct { + BaseNickname cfg.BaseUniNick `json:"base_nickname"` + BaseName string `json:"base_name"` + SystemName string `json:"system_name"` + Region string `json:"region_name"` + FactionName string `json:"faction_name"` + BasePos cfg.Vector `json:"base_pos"` + SectorCoord string `json:"sector_coord"` +} + +func (e *Exporter) GetRegionName(system *universe_mapped.System) string { + return e.Configs.GetRegionName(system) +} + +func (e *Exporter) GetBaseInfo(base_nickname universe_mapped.BaseNickname) BaseInfo { + var result BaseInfo = BaseInfo{ + BaseNickname: cfg.BaseUniNick(base_nickname), + } + universe_base, found_universe_base := e.Configs.Universe.BasesMap[universe_mapped.BaseNickname(base_nickname)] + + if !found_universe_base { + return result + } + + result.BaseName = e.GetInfocardName(universe_base.StridName.Get(), string(base_nickname)) + system_nickname := universe_base.System.Get() + + system, system_ok := e.Configs.Universe.SystemMap[universe_mapped.SystemNickname(system_nickname)] + if system_ok { + result.SystemName = e.GetInfocardName(system.StridName.Get(), system_nickname) + result.Region = e.GetRegionName(system) + } + + var reputation_nickname string + if system, ok := e.Configs.Systems.SystemsMap[universe_base.System.Get()]; ok { + for _, system_base := range system.Bases { + if system_base.IdsName.Get() != universe_base.StridName.Get() { + continue + } + + reputation_nickname = system_base.RepNickname.Get() + result.BasePos = system_base.Pos.Get() + } + + } + + result.SectorCoord = VectorToSectorCoord(system, result.BasePos) + + var factionName string + if group, exists := e.Configs.InitialWorld.GroupsMap[reputation_nickname]; exists { + factionName = e.GetInfocardName(group.IdsName.Get(), reputation_nickname) + } + + result.FactionName = factionName + + return result +} + +func (e *Exporter) FilterToUsefulCommodities(commodities []*Commodity) []*Commodity { + var items []*Commodity = make([]*Commodity, 0, len(commodities)) + for _, item := range commodities { + if !e.Buyable(item.Bases) { + continue + } + items = append(items, item) + } + return items +} diff --git a/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/engines.go b/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/engines.go new file mode 100644 index 0000000..6ecc3e1 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/engines.go @@ -0,0 +1,124 @@ +package configs_export + +import ( + "github.com/darklab8/fl-darkstat/configs/cfg" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/equipment_mapped/equip_mapped" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/initialworld/flhash" +) + +func (g Engine) GetTechCompat() *DiscoveryTechCompat { return g.DiscoveryTechCompat } + +type Engine struct { + Name string `json:"name"` + Price int `json:"price"` + + CruiseSpeed int `json:"cruise_speed"` + CruiseChargeTime int `json:"cruise_charge_time"` + LinearDrag int `json:"linear_drag"` + MaxForce int `json:"max_force"` + ReverseFraction float64 `json:"reverse_fraction"` + ImpulseSpeed float64 `json:"impulse_speed"` + + HpType string `json:"hp_type"` + HpTypeHash flhash.HashCode `json:"-" swaggerignore:"true"` + FlameEffect string `json:"flame_effect"` + FlameEffectHash flhash.HashCode `json:"-" swaggerignore:"true"` + TrailEffect string `json:"trail_effect"` + TrailEffectHash flhash.HashCode `json:"-" swaggerignore:"true"` + + Nickname string `json:"nickname"` + NicknameHash flhash.HashCode `json:"nickname_hash" format:"int64"` + NameID int `json:"name_id"` + InfoID int `json:"info_id"` + + Bases map[cfg.BaseUniNick]*MarketGood `json:"-" swaggerignore:"true"` + *DiscoveryTechCompat `json:"-" swaggerignore:"true"` + Mass float64 `json:"mass"` +} + +func (b Engine) GetNickname() string { return string(b.Nickname) } + +func (b Engine) GetBases() map[cfg.BaseUniNick]*MarketGood { return b.Bases } + +func (b Engine) GetDiscoveryTechCompat() *DiscoveryTechCompat { return b.DiscoveryTechCompat } + +func (e *Exporter) GetEngineSpeed(engine_info *equip_mapped.Engine) int { + if cruise_speed, ok := engine_info.CruiseSpeed.GetValue(); ok { + return cruise_speed + } else { + if cruise_speed, ok := e.Configs.Consts.EngineEquipConsts.CRUISING_SPEED.GetValue(); ok { + return cruise_speed + } + } + return 350 +} + +func (e *Exporter) GetEngines(ids []*Tractor) []Engine { + var engines []Engine + + for _, engine_info := range e.Configs.Equip.Engines { + engine := Engine{ + Bases: make(map[cfg.BaseUniNick]*MarketGood), + } + engine.Mass, _ = engine_info.Mass.GetValue() + + engine.Nickname = engine_info.Nickname.Get() + engine.CruiseSpeed = e.GetEngineSpeed(engine_info) + engine.CruiseChargeTime, _ = engine_info.CruiseChargeTime.GetValue() + engine.LinearDrag = engine_info.LinearDrag.Get() + engine.MaxForce = engine_info.MaxForce.Get() + engine.ReverseFraction = engine_info.ReverseFraction.Get() + linear_drag_for_calc := engine.LinearDrag + if linear_drag_for_calc == 0 { + linear_drag_for_calc = 1 + } + engine.ImpulseSpeed = float64(engine.MaxForce) / float64(linear_drag_for_calc) + + engine.HpType, _ = engine_info.HpType.GetValue() + engine.FlameEffect, _ = engine_info.FlameEffect.GetValue() + engine.TrailEffect, _ = engine_info.TrailEffect.GetValue() + + engine.NameID = engine_info.IdsName.Get() + engine.InfoID = engine_info.IdsInfo.Get() + + if good_info, ok := e.Configs.Goods.GoodsMap[engine.Nickname]; ok { + if price, ok := good_info.Price.GetValue(); ok { + engine.Price = price + engine.Bases = e.GetAtBasesSold(GetCommodityAtBasesInput{ + Nickname: good_info.Nickname.Get(), + Price: price, + }) + } + } + + engine.Name = e.GetInfocardName(engine.NameID, engine.Nickname) + + e.exportInfocards(InfocardKey(engine.Nickname), engine.InfoID) + + engine.DiscoveryTechCompat = CalculateTechCompat(e.Configs.Discovery, ids, engine.Nickname) + engine.NicknameHash = flhash.HashNickname(engine.Nickname) + engine.HpTypeHash = flhash.HashNickname(engine.HpType) + engine.FlameEffectHash = flhash.HashNickname(engine.FlameEffect) + engine.TrailEffectHash = flhash.HashNickname(engine.TrailEffect) + + e.Hashes[engine.Nickname] = engine.NicknameHash + e.Hashes[engine.HpType] = engine.HpTypeHash + e.Hashes[engine.FlameEffect] = engine.FlameEffectHash + e.Hashes[engine.TrailEffect] = engine.TrailEffectHash + + engines = append(engines, engine) + } + return engines +} + +func (e *Exporter) FilterToUsefulEngines(engines []Engine) []Engine { + var buyable_engines []Engine = make([]Engine, 0, len(engines)) + for _, engine := range engines { + if !e.Buyable(engine.Bases) { + continue + } + buyable_engines = append(buyable_engines, engine) + } + + return buyable_engines +} diff --git a/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/exporter.go b/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/exporter.go new file mode 100644 index 0000000..ad2bd59 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/exporter.go @@ -0,0 +1,448 @@ +package configs_export + +import ( + "strings" + "sync" + + "github.com/darklab8/fl-darkstat/configs/cfg" + "github.com/darklab8/fl-darkstat/configs/configs_mapped" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/initialworld/flhash" + "github.com/darklab8/fl-darkstat/configs/configs_settings/logus" + "github.com/darklab8/fl-darkstat/darkstat/configs_export/trades" + "github.com/darklab8/fl-darkstat/darkstat/settings" +) + +type InfocardKey string + +type InfocardPhrase struct { + Phrase string `json:"phrase"` + Link *string `json:"link"` + Bold bool `json:"bold"` +} + +type InfocardLine struct { + Phrases []InfocardPhrase `json:"phrases"` +} + +func (i InfocardLine) ToStr() string { + var sb strings.Builder + for _, phrase := range i.Phrases { + sb.WriteString(phrase.Phrase) + } + return sb.String() +} + +func NewInfocardSimpleLine(line string) InfocardLine { + return InfocardLine{Phrases: []InfocardPhrase{{Phrase: line}}} +} + +func NewInfocardBuilder() InfocardBuilder { + return InfocardBuilder{} +} +func (i *InfocardBuilder) WriteLine(phrases ...InfocardPhrase) { + i.Lines = append(i.Lines, InfocardLine{Phrases: phrases}) +} +func (i *InfocardBuilder) WriteLineStr(phrase_strs ...string) { + var phrases []InfocardPhrase + for _, phrase := range phrase_strs { + phrases = append(phrases, InfocardPhrase{Phrase: phrase}) + } + i.Lines = append(i.Lines, InfocardLine{Phrases: phrases}) +} + +type InfocardBuilder struct { + Lines Infocard +} + +type Infocard []InfocardLine + +func (i Infocard) StringsJoin(delimiter string) string { + var sb strings.Builder + + for _, line := range i { + for _, phrase := range line.Phrases { + sb.WriteString(phrase.Phrase) + } + sb.WriteString(delimiter) + } + return sb.String() +} + +func (e *Exporter) exportInfocards(nickname InfocardKey, infocard_ids ...int) { + if _, ok := e.Infocards[InfocardKey(nickname)]; ok { + return + } + + for _, info_id := range infocard_ids { + if value, ok := e.Configs.Infocards.Infocards[info_id]; ok { + for _, line := range value.Lines { + e.Infocards[InfocardKey(nickname)] = append(e.Infocards[InfocardKey(nickname)], NewInfocardSimpleLine(line)) + } + + } + } + + if len(e.Infocards[InfocardKey(nickname)]) == 0 { + e.Infocards[InfocardKey(nickname)] = []InfocardLine{NewInfocardSimpleLine("no infocard")} + } +} + +type Infocards map[InfocardKey]Infocard + +type Exporter struct { + Configs *configs_mapped.MappedConfigs + Hashes map[string]flhash.HashCode + + Bases []*Base + TradeBases []*Base + TravelBases []*Base + + MiningOperations []*Base + useful_bases_by_nick map[cfg.BaseUniNick]bool + + ship_speeds trades.ShipSpeeds + Transport *GraphResults + Freighter *GraphResults + Frigate *GraphResults + + Factions []Faction + Infocards Infocards + Commodities []*Commodity + Guns []Gun + Missiles []Gun + Mines []Mine + Shields []Shield + Thrusters []Thruster + Ships []Ship + Tractors []*Tractor + TractorsByID map[cfg.TractorID]*Tractor + Cloaks []Cloak + Engines []Engine + CMs []CounterMeasure + Scanners []Scanner + Ammos []Ammo + PoBs []*PoB + PoBGoods []*PoBGood + + findable_in_loot_cache map[string]bool + craftable_cached map[string]bool + pob_buyable_cache map[string][]*PobShopItem +} + +type OptExport func(e *Exporter) + +func NewExporter(configs *configs_mapped.MappedConfigs, opts ...OptExport) *Exporter { + e := &Exporter{ + Configs: configs, + Infocards: map[InfocardKey]Infocard{}, + ship_speeds: trades.VanillaSpeeds, + Hashes: make(map[string]flhash.HashCode), + } + + for _, opt := range opts { + opt(e) + } + return e +} + +type GraphResults struct { + e *Exporter + Graph *trades.GameGraph + Time [][]int + Parents [][]trades.Parent +} + +func NewGraphResults( + e *Exporter, + avgCruiserSpeed int, + can_visit_freighter_only_jhs trades.WithFreighterPaths, + mining_bases_by_system map[string][]trades.ExtraBase, + graph_options trades.MappingOptions, +) *GraphResults { + logus.Log.Info("mapping configs to graph") + graph := trades.MapConfigsToFGraph( + e.Configs, + avgCruiserSpeed, + can_visit_freighter_only_jhs, + mining_bases_by_system, + graph_options, + ) + logus.Log.Info("new dijkstra apsp from graph") + dijkstra_apsp := trades.NewDijkstraApspFromGraph(graph) + logus.Log.Info("calculating dijkstra") + dists, parents := dijkstra_apsp.DijkstraApsp() + + graph.WipeMatrix() + return &GraphResults{ + e: e, + Graph: graph, + Time: dists, + Parents: parents, + } +} + +type ExportOptions struct { + trades.MappingOptions +} + +func (e *Exporter) Export(options ExportOptions) *Exporter { + var wg sync.WaitGroup + + logus.Log.Info("getting bases") + e.Bases = e.GetBases() + useful_bases := FilterToUserfulBases(e.Bases) + e.useful_bases_by_nick = make(map[cfg.BaseUniNick]bool) + for _, base := range useful_bases { + e.useful_bases_by_nick[base.Nickname] = true + } + e.useful_bases_by_nick[pob_crafts_nickname] = true + e.useful_bases_by_nick[BaseLootableNickname] = true + + e.Commodities = e.GetCommodities() + EnhanceBasesWithServerOverrides(e.Bases, e.Commodities) + + e.MiningOperations = e.GetOres(e.Commodities) + if e.Configs.Discovery != nil { + e.PoBs = e.GetPoBs() + e.PoBGoods = e.GetPoBGoods(e.PoBs) + } + + extra_graph_bases := make(map[string][]trades.ExtraBase) + for _, base := range e.MiningOperations { + extra_graph_bases[base.SystemNickname] = append(extra_graph_bases[base.SystemNickname], trades.ExtraBase{ + Pos: base.Pos, + Nickname: base.Nickname, + }) + } + for _, base := range e.PoBs { + if base.SystemNick == nil || base.Pos == nil { + continue + } + extra_graph_bases[*base.SystemNick] = append(extra_graph_bases[*base.SystemNick], trades.ExtraBase{ + Pos: *StrPosToVectorPos(*base.Pos), + Nickname: cfg.BaseUniNick(base.Nickname), + }) + } + if e.Configs.Discovery != nil { + e.ship_speeds = trades.DiscoverySpeeds + } + + if e.Configs.FLSR != nil { + e.ship_speeds = trades.FLSRSpeeds + } + + if !settings.Env.IsDisabledTradeRouting { + + wg.Add(1) + go func() { + logus.Log.Info("graph launching for tranposrt") + + e.Transport = NewGraphResults(e, e.ship_speeds.AvgTransportCruiseSpeed, trades.WithFreighterPaths(false), extra_graph_bases, options.MappingOptions) + // e.Freighter = e.Transport + // e.Frigate = e.Transport + wg.Done() + logus.Log.Info("graph finished for tranposrt") + + }() + wg.Add(1) + go func() { + e.Freighter = NewGraphResults(e, e.ship_speeds.AvgFreighterCruiseSpeed, trades.WithFreighterPaths(true), extra_graph_bases, options.MappingOptions) + wg.Done() + }() + wg.Add(1) + go func() { + e.Frigate = NewGraphResults(e, e.ship_speeds.AvgFrigateCruiseSpeed, trades.WithFreighterPaths(false), extra_graph_bases, options.MappingOptions) + wg.Done() + }() + } + + logus.Log.Info("getting get tractors") + + e.Tractors = e.GetTractors() + e.TractorsByID = make(map[cfg.TractorID]*Tractor) + for _, tractor := range e.Tractors { + e.TractorsByID[tractor.Nickname] = tractor + } + e.Factions = e.GetFactions(e.Bases) + e.Bases = e.GetMissions(e.Bases, e.Factions) + + logus.Log.Info("getting shields") + + e.Shields = e.GetShields(e.Tractors) + buyable_shield_tech := e.GetBuyableShields(e.Shields) + e.Guns = e.GetGuns(e.Tractors, buyable_shield_tech) + e.Missiles = e.GetMissiles(e.Tractors, buyable_shield_tech) + e.Mines = e.GetMines(e.Tractors) + e.Thrusters = e.GetThrusters(e.Tractors) + logus.Log.Info("getting ships") + e.Ships = e.GetShips(e.Tractors, e.TractorsByID, e.Thrusters) + e.Engines = e.GetEngines(e.Tractors) + e.Cloaks = e.GetCloaks(e.Tractors) + e.CMs = e.GetCounterMeasures(e.Tractors) + e.Scanners = e.GetScanners(e.Tractors) + logus.Log.Info("getting ammo") + + e.Ammos = e.GetAmmo(e.Tractors) + logus.Log.Info("waiting for graph to finish") + + wg.Wait() + + logus.Log.Info("getting pob to bases") + BasesFromPobs := e.PoBsToBases(e.PoBs) + TradeBases := append(e.Bases, BasesFromPobs...) + e.TradeBases, e.Commodities = e.TradePaths(TradeBases, e.Commodities) + e.MiningOperations, e.Commodities = e.TradePaths(e.MiningOperations, e.Commodities) + e.TradeBases = e.AllRoutes(TradeBases) + for _, base := range e.TradeBases { + e.TravelBases = append(e.TravelBases, base) + } + + for _, system := range e.Configs.Systems.Systems { + for zone_nick := range system.ZonesByNick { + e.Hashes[zone_nick] = flhash.HashNickname(zone_nick) + } + for _, object := range system.Objects { + nickname, _ := object.Nickname.GetValue() + e.Hashes[nickname] = flhash.HashNickname(nickname) + } + } + for _, good := range e.Configs.Goods.Goods { + nickname, _ := good.Nickname.GetValue() + e.Hashes[nickname] = flhash.HashNickname(nickname) + } + + e.EnhanceBasesWithIsTransportReachable(e.Bases, e.Transport, e.Freighter) + e.Bases = e.EnhanceBasesWithPobCrafts(e.Bases) + e.Bases = e.EnhanceBasesWithLoot(e.Bases) + logus.Log.Info("finished exporting") + + return e +} + +func (e *Exporter) EnhanceBasesWithIsTransportReachable( + bases []*Base, + transports_graph *GraphResults, + frighter_graph *GraphResults, +) { + reachable_base_example := "li01_01_base" + tg := transports_graph + fg := frighter_graph + + for _, base := range bases { + base_nickname := base.Nickname.ToStr() + if trades.GetTimeMs2(tg.Graph, tg.Time, reachable_base_example, base_nickname) >= trades.INFthreshold { + base.IsTransportUnreachable = true + } + if trades.GetTimeMs2(fg.Graph, fg.Time, reachable_base_example, base_nickname) < trades.INFthreshold { + base.Reachable = true + } + } + + enhance_with_transport_unrechability := func(Bases map[cfg.BaseUniNick]*MarketGood) { + for _, base := range Bases { + if trades.GetTimeMs2(tg.Graph, tg.Time, reachable_base_example, string(base.BaseNickname)) >= trades.INF/2 { + base.IsTransportUnreachable = true + } + } + } + + for _, item := range e.Commodities { + enhance_with_transport_unrechability(item.Bases) + } + for _, item := range e.Guns { + enhance_with_transport_unrechability(item.Bases) + } + for _, item := range e.Missiles { + enhance_with_transport_unrechability(item.Bases) + } + for _, item := range e.Mines { + enhance_with_transport_unrechability(item.Bases) + } + for _, item := range e.Shields { + enhance_with_transport_unrechability(item.Bases) + } + for _, item := range e.Thrusters { + enhance_with_transport_unrechability(item.Bases) + } + for _, item := range e.Ships { + enhance_with_transport_unrechability(item.Bases) + } + for _, item := range e.Tractors { + enhance_with_transport_unrechability(item.Bases) + } + for _, item := range e.Engines { + enhance_with_transport_unrechability(item.Bases) + } + for _, item := range e.CMs { + enhance_with_transport_unrechability(item.Bases) + } + for _, item := range e.Scanners { + enhance_with_transport_unrechability(item.Bases) + } + for _, item := range e.Ammos { + enhance_with_transport_unrechability(item.Bases) + } + for _, item := range e.Cloaks { + enhance_with_transport_unrechability(item.Bases) + } +} + +func Export(configs *configs_mapped.MappedConfigs, options ExportOptions) *Exporter { + return NewExporter(configs).Export(options) +} + +func Empty(phrase string) bool { + for _, letter := range phrase { + if letter != ' ' { + return false + } + } + return true +} + +func (e *Exporter) Buyable(Bases map[cfg.BaseUniNick]*MarketGood) bool { + for _, base := range Bases { + + if e.useful_bases_by_nick != nil { + if _, ok := e.useful_bases_by_nick[base.BaseNickname]; ok { + return true + } + } + } + + return false +} + +func Buyable(Bases map[cfg.BaseUniNick]*MarketGood) bool { + return len(Bases) > 0 +} + +type DiscoveryTechCompat struct { + TechcompatByID map[cfg.TractorID]float64 `json:"techchompat_by_id"` + TechCell string `json:"tech_cell"` +} + +func CalculateTechCompat(Discovery *configs_mapped.DiscoveryConfig, ids []*Tractor, nickname string) *DiscoveryTechCompat { + if Discovery == nil { + return nil + } + + techcompat := &DiscoveryTechCompat{ + TechcompatByID: make(map[cfg.TractorID]float64), + } + techcompat.TechcompatByID[""] = Discovery.Techcompat.GetCompatibilty(nickname, "") + + for _, id := range ids { + techcompat.TechcompatByID[id.Nickname] = Discovery.Techcompat.GetCompatibilty(nickname, id.Nickname) + } + + if compat, ok := Discovery.Techcompat.CompatByItem[nickname]; ok { + techcompat.TechCell = compat.TechCell + } + + return techcompat +} + +func (e *Exporter) GetInfocardName(ids_name int, nickname string) string { + return e.Configs.GetInfocardName(ids_name, nickname) +} diff --git a/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/factions.go b/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/factions.go new file mode 100644 index 0000000..7c9a8b7 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/factions.go @@ -0,0 +1,151 @@ +package configs_export + +import ( + "fmt" + "strings" + + "github.com/darklab8/fl-darkstat/configs/cfg" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/initialworld/flhash" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/missions_mapped/mbases_mapped" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/universe_mapped" +) + +type Reputation struct { + Name string `json:"name"` + Rep float64 `json:"rep"` + Empathy float64 `json:"empathy"` + Nickname string `json:"nickname"` +} + +type Faction struct { + Name string `json:"name"` + ShortName string `json:"short_name"` + Nickname string `json:"nickname"` + NicknameHash flhash.HashCode `json:"-" swaggerignore:"true"` + + ObjectDestruction float64 `json:"object_destruction"` + MissionSuccess float64 `json:"mission_success"` + MissionFailure float64 `json:"mission_failure"` + MissionAbort float64 `json:"mission_abort"` + + InfonameID int `json:"infoname_id"` + InfocardID int `json:"infocard_id"` + InfocardKey InfocardKey `json:"-" swaggerignore:"true"` + Reputations []Reputation `json:"reputations"` + Bribes []Bribe `json:"bribe"` +} + +func (b Faction) GetNickname() string { return string(b.Nickname) } + +type Bribe struct { + BaseNickname string `json:"base_nickname"` + BaseInfo + Chance float64 `json:"chance"` +} + +func (e *Exporter) GetFactions(bases []*Base) []Faction { + var factions []Faction = make([]Faction, 0, 100) + + var basemap map[cfg.BaseUniNick]*Base = make(map[cfg.BaseUniNick]*Base) + for _, base := range bases { + basemap[base.Nickname] = base + } + + // for faction, at base, chance + faction_rephacks := mbases_mapped.FactionBribes(e.Configs.MBases) + + for _, group := range e.Configs.InitialWorld.Groups { + var nickname string = group.Nickname.Get() + faction := Faction{ + Nickname: nickname, + NicknameHash: flhash.HashFaction(nickname), + InfonameID: group.IdsName.Get(), + InfocardID: group.IdsInfo.Get(), + InfocardKey: InfocardKey(nickname), + } + e.Hashes[faction.Nickname] = faction.NicknameHash + + if rephacks, ok := faction_rephacks[nickname]; ok { + + for base, chance := range rephacks { + rephack := Bribe{ + BaseNickname: base, + Chance: chance, + BaseInfo: e.GetBaseInfo(universe_mapped.BaseNickname(base)), + } + + faction.Bribes = append(faction.Bribes, rephack) + } + } + faction.Name = e.GetInfocardName(group.IdsName.Get(), faction.Nickname) + + e.exportInfocards(InfocardKey(nickname), group.IdsInfo.Get()) + + faction.ShortName = e.GetInfocardName(group.IdsShortName.Get(), faction.Nickname) + + empathy_rates, empathy_exists := e.Configs.Empathy.RepoChangeMap[faction.Nickname] + + if empathy_exists { + faction.ObjectDestruction = empathy_rates.ObjectDestruction.Get() + faction.MissionSuccess = empathy_rates.MissionSuccess.Get() + faction.MissionFailure = empathy_rates.MissionFailure.Get() + faction.MissionAbort = empathy_rates.MissionAbort.Get() + } + + for _, reputation := range group.Relationships { + rep_to_add := &Reputation{} + rep_to_add.Nickname = reputation.TargetNickname.Get() + rep_to_add.Rep = reputation.Rep.Get() + + target_faction := e.Configs.InitialWorld.GroupsMap[rep_to_add.Nickname] + defer func() { + if r := recover(); r != nil { + fmt.Println("Recovered in f", r) + fmt.Println("recovered rep_to_add.Nickname", rep_to_add.Nickname) + panic(r) + } + }() + + if target_faction != nil { + rep_to_add.Name = e.GetInfocardName(target_faction.IdsName.Get(), rep_to_add.Nickname) + } + + if empathy_exists { + if empathy_rate, ok := empathy_rates.EmpathyRatesMap[rep_to_add.Nickname]; ok { + rep_to_add.Empathy = empathy_rate.RepoChange.Get() + } + } + + faction.Reputations = append(faction.Reputations, *rep_to_add) + } + + factions = append(factions, faction) + + } + + return factions +} + +func FilterToUsefulFactions(factions []Faction) []Faction { + var useful_factions []Faction = make([]Faction, 0, len(factions)) + for _, item := range factions { + if Empty(item.Name) || strings.Contains(item.Name, "_grp") { + continue + } + + useful_factions = append(useful_factions, item) + } + return useful_factions +} + +func FilterToUsefulBribes(factions []Faction) []Faction { + var useful_factions []Faction = make([]Faction, 0, len(factions)) + for _, item := range factions { + if len(item.Bribes) == 0 { + continue + } + + useful_factions = append(useful_factions, item) + } + return useful_factions +} diff --git a/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/goods.go b/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/goods.go new file mode 100644 index 0000000..7804434 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/goods.go @@ -0,0 +1,144 @@ +package configs_export + +import ( + "math" + + "github.com/darklab8/fl-darkstat/configs/cfg" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/initialworld/flhash" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/universe_mapped" + "github.com/darklab8/go-utils/utils/ptr" +) + +func NameWithSpacesOnly(word string) bool { + for _, ch := range word { + if ch != ' ' { + return false + } + } + return true +} + +type GoodInfo struct { + Nickname string `json:"nickname"` + ShipNickname string `json:"ship_nickname"` // market good can be ship package, if it is, then ship nickname bought by package is specified + NicknameHash flhash.HashCode `json:"nickname_hash" format:"int64"` + Name string `json:"name"` + PriceBase int `json:"price_base"` + HpType string `json:"hp_type"` + Category string `json:"category"` +} + +func (e *Exporter) GetGoodInfo(good_nickname string) GoodInfo { + var info GoodInfo = GoodInfo{ + Nickname: good_nickname, + NicknameHash: flhash.HashNickname(good_nickname), + } + if good, found_good := e.Configs.Goods.GoodsMap[good_nickname]; found_good { + info.PriceBase = good.Price.Get() + + info.Category = good.Category.Get() + switch info.Category { + default: + if equip, ok := e.Configs.Equip.ItemsMap[good_nickname]; ok { + info.Category = equip.Category + info.Name = e.GetInfocardName(equip.IdsName.Get(), good_nickname) + + e.exportInfocards(InfocardKey(good_nickname), equip.IdsInfo.Get()) + } + case "ship": + ship := e.Configs.Goods.ShipsMap[good.Nickname.Get()] + + ship_hull := e.Configs.Goods.ShipHullsMap[ship.Hull.Get()] + info.PriceBase = ship_hull.Price.Get() + + // Infocard data + info.ShipNickname = ship_hull.Ship.Get() + shiparch := e.Configs.Shiparch.ShipsMap[info.ShipNickname] + + info.Name = e.GetInfocardName(shiparch.IdsName.Get(), info.ShipNickname) + + // e.exportInfocards(InfocardKey(market_good_nickname), + // shiparch.IdsInfo.Get(), shiparch.IdsInfo1.Get(), shiparch.IdsInfo2.Get(), shiparch.IdsInfo3.Get()) + e.exportInfocards(InfocardKey(good_nickname), + shiparch.IdsInfo1.Get(), shiparch.IdsInfo.Get()) + } + + if gun, ok := e.Configs.Equip.GunMap[good_nickname]; ok { + info.HpType, _ = gun.HPGunType.GetValue() + } + if shield, ok := e.Configs.Equip.ShidGenMap[good_nickname]; ok { + info.HpType, _ = shield.HpType.GetValue() + } + if engine, ok := e.Configs.Equip.EnginesMap[good_nickname]; ok { + info.HpType, _ = engine.HpType.GetValue() + } + } + if NameWithSpacesOnly(info.Name) { + info.Name = "" + } + + return info +} + +func (e *Exporter) getMarketGoods() map[cfg.BaseUniNick]map[CommodityKey]*MarketGood { + + var goods_per_base map[cfg.BaseUniNick]map[CommodityKey]*MarketGood = make(map[cfg.BaseUniNick]map[CommodityKey]*MarketGood) + + for _, base_good := range e.Configs.Market.BaseGoods { + base_nickname := cfg.BaseUniNick(base_good.Base.Get()) + + var MarketGoods map[CommodityKey]*MarketGood + if market_goods, ok := goods_per_base[base_nickname]; ok { + MarketGoods = market_goods + } else { + MarketGoods = make(map[CommodityKey]*MarketGood) + } + for _, market_good := range base_good.MarketGoods { + + var market_good_nickname string = market_good.Nickname.Get() + + good_to_add := &MarketGood{ + GoodInfo: e.GetGoodInfo(market_good_nickname), + BaseInfo: e.GetBaseInfo(universe_mapped.BaseNickname(base_nickname)), + LevelRequired: market_good.LevelRequired.Get(), + RepRequired: market_good.RepRequired.Get(), + BaseSells: market_good.BaseSells(), + ShipClass: -1, + } + good_to_add.PriceBaseSellsFor = int(math.Floor(float64(good_to_add.PriceBase) * market_good.PriceModifier.Get())) + + e.Hashes[market_good_nickname] = good_to_add.NicknameHash + + if good_to_add.Category == "commodity" { + + if e.Configs.Discovery != nil { + good_to_add.PriceBaseBuysFor = ptr.Ptr(market_good.BaseSellsIPositiveAndDiscoSellPrice.Get()) + } else { + good_to_add.PriceBaseBuysFor = ptr.Ptr(good_to_add.PriceBaseSellsFor) + } + equipment := e.Configs.Equip.CommoditiesMap[market_good_nickname] + + for _, volume := range equipment.Volumes { + good_to_add2 := good_to_add + good_to_add2.Volume = volume.Volume.Get() + good_to_add2.ShipClass = volume.GetShipClass() + MarketGoods[GetCommodityKey(good_to_add2.Nickname, good_to_add2.ShipClass)] = good_to_add2 + } + + } else { + MarketGoods[GetCommodityKey(market_good_nickname, good_to_add.ShipClass)] = good_to_add + } + } + + if !e.TraderExists(string(base_nickname)) { + for good_key, good := range MarketGoods { + if good.Category == "commodity" { + delete(MarketGoods, good_key) + } + } + } + + goods_per_base[base_nickname] = MarketGoods + } + return goods_per_base +} diff --git a/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/guns.go b/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/guns.go new file mode 100644 index 0000000..948404e --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/guns.go @@ -0,0 +1,405 @@ +package configs_export + +import ( + "errors" + "fmt" + "strconv" + "strings" + + "github.com/darklab8/fl-darkstat/configs/cfg" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/equipment_mapped/equip_mapped" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/initialworld/flhash" + "github.com/darklab8/fl-darkstat/configs/configs_settings/logus" + "github.com/darklab8/go-typelog/typelog" + "github.com/darklab8/go-utils/utils/ptr" +) + +type DamageBonus struct { + Type string + Modifier float64 +} + +type GunDetailed struct { + FlashParticleName string + ConstEffect string + MunitionHitEffect string +} + +func (g Gun) GetTechCompat() *DiscoveryTechCompat { return g.DiscoveryTechCompat } + +type Gun struct { + Nickname string `json:"nickname"` + NicknameHash flhash.HashCode `json:"nickname_hash" format:"int64"` + MunitionHash flhash.HashCode `json:"munition_hash" format:"int64"` + HpTypeHash flhash.HashCode `json:"hp_type_hash" format:"int64"` + Name string `json:"name"` + Type string `json:"type"` + Price int `json:"price"` + Class string `json:"class"` + HpType string `json:"hp_type"` + IdsName int `json:"ids_name"` + IdsInfo int `json:"ids_info"` + Volume float64 `json:"volume"` + + HitPts string `json:"hit_pts"` + PowerUsage float64 `json:"power_usage"` + Refire float64 `json:"refire"` + Range float64 `json:"range"` + Toughness float64 `json:"toughness"` + IsAutoTurret bool `json:"is_auto_turret"` + Lootable bool `json:"lootable"` + + RequiredAmmo bool `json:"required_ammo"` + // AmmoPrice int + // AmmoBases []*GoodAtBase + // AmmoName string + HullDamage int `json:"hull_damage"` + EnergyDamage int `json:"energy_damage"` + ShieldDamage int `json:"shield_damage"` + AvgShieldDamage int `json:"avg_shield_damage"` + DamageType string `json:"damage_type"` + LifeTime float64 `json:"life_time"` + Speed float64 `json:"speed"` + GunTurnRate float64 `json:"gun_turn_rate"` + DispersionAngle float64 `json:"dispersion_angle"` + + HullDamagePerSec float64 `json:"hull_damage_per_sec"` + AvgShieldDamagePerSec float64 `json:"avg_shield_damage_per_sec"` + EnergyDamagePerSec float64 `json:"energy_damage_per_sec"` + PowerUsagePerSec float64 `json:"power_usage_per_sec"` + AvgEfficiency float64 `json:"avg_efficiency"` + HullEfficiency float64 `json:"hull_efficiency"` + ShieldEfficiency float64 `json:"shield_efficiency"` + EnergyDamageEfficiency float64 `json:"energy_damage_efficiency"` + + Bases map[cfg.BaseUniNick]*MarketGood `json:"-" swaggerignore:"true"` + DamageBonuses []DamageBonus `json:"damage_bonuses"` + + Missile + *DiscoveryTechCompat `json:"-" swaggerignore:"true"` + GunDetailed + + NumBarrels *int `json:"num_barrels,omitempty"` + BurstFire *BurstFire `json:"burst_fire,omitempty"` + AmmoLimit AmmoLimit `json:"ammo_limit,omitempty"` + + Mass float64 `json:"mass"` + + DiscoGun *DiscoGun `json:"disco_gun"` +} + +func (b Gun) GetNickname() string { return string(b.Nickname) } + +func (b Gun) GetBases() map[cfg.BaseUniNick]*MarketGood { return b.Bases } + +func (b Gun) GetDiscoveryTechCompat() *DiscoveryTechCompat { return b.DiscoveryTechCompat } + +type DiscoGun struct { + ArmorPen float64 `json:"armor_pen"` +} + +type BurstFire struct { + SustainedRefire float64 + Ammo int + ReloadTime float64 + + SustainedHullDamagePerSec float64 + SustainedAvgShieldDamagePerSec float64 + SustainedEnergyDamagePerSec float64 + SustainedPowerUsagePerSec float64 +} + +func getGunClass(gun_info *equip_mapped.Gun) string { + var gun_class string + if gun_type, ok := gun_info.HPGunType.GetValue(); ok { + splitted := strings.Split(gun_type, "_") + if len(splitted) > 0 { + class := splitted[len(splitted)-1] + if _, err := strconv.Atoi(class); err == nil { + gun_class = class + } + } + } + return gun_class +} + +func (e *Exporter) getGunInfo(gun_info *equip_mapped.Gun, ids []*Tractor, buyable_ship_tech map[string]bool) (Gun, error) { + gun_nickname := gun_info.Nickname.Get() + defer func() { + if r := recover(); r != nil { + fmt.Println("Recovered in f", r) + fmt.Println("recovered gun_nickname", gun_nickname) + panic(r) + } + }() + + gun := Gun{ + Nickname: gun_nickname, + NicknameHash: flhash.HashNickname(gun_nickname), + IdsName: gun_info.IdsName.Get(), + IdsInfo: gun_info.IdsInfo.Get(), + Class: getGunClass(gun_info), + HitPts: gun_info.HitPts.Get(), + PowerUsage: gun_info.PowerUsage.Get(), + Refire: float64(1 / gun_info.RefireDelay.Get()), + Speed: gun_info.MuzzleVelosity.Get(), + Toughness: gun_info.Toughness.Get(), + Lootable: gun_info.Lootable.Get(), + Bases: make(map[cfg.BaseUniNick]*MarketGood), + } + + gun.Mass, _ = gun_info.Mass.GetValue() + + num_barrels := 1 + if num_barrels_value, ok := gun_info.NumBarrels.GetValue(); ok { + num_barrels = num_barrels_value + gun.NumBarrels = ptr.Ptr(num_barrels_value) + } + + if ammo, ok := gun_info.BurstAmmo.GetValue(); ok { + gun.BurstFire = &BurstFire{ + Ammo: ammo, + } + gun.BurstFire.ReloadTime = gun_info.BurstReload.Get() + + // (magCapacity * RefireDelay + Reload time) / Mag Capacity = This should be average refire delay + + gun.BurstFire.SustainedRefire = 1 / ((gun_info.RefireDelay.Get()*float64(gun.BurstFire.Ammo) + gun.BurstFire.ReloadTime) / float64(gun.BurstFire.Ammo)) + } + + gun.IsAutoTurret, _ = gun_info.IsAutoTurret.GetValue() + gun.Volume, _ = gun_info.Volume.GetValue() + + gun.HpType, _ = gun_info.HPGunType.GetValue() + gun.HpTypeHash = flhash.HashNickname(gun.HpType) + + munition, found_munition := e.Configs.Equip.MunitionMap[gun_info.ProjectileArchetype.Get()] + + if e.Configs.FLSR != nil && !found_munition && gun.Nickname == "gd_ww_turret_laser_light02" && gun_info.ProjectileArchetype.Get() == "gd_ww_laser_light02_ammo" { + logus.Log.Error("gun does not have defined munition", + typelog.Any("nickname", gun.Nickname), + typelog.Any("projectile_archetype", gun_info.ProjectileArchetype.Get())) + return gun, errors.New("not defined munition") + } + + if gun.Nickname == "gd_ww_turret_laser_light02" { + logus.Log.Warn("FLSR broken gun potentially", + typelog.String("gun.Nickname", gun.Nickname), + typelog.String("projectile", gun_info.ProjectileArchetype.Get()), + typelog.Bool("is_flsr", e.Configs.FLSR != nil), + typelog.Bool("found_munition", found_munition), + ) + } + + gun.MunitionHash = flhash.HashNickname(munition.Nickname.Get()) + gun.FlashParticleName, _ = gun_info.FlashParticleName.GetValue() + gun.ConstEffect, _ = munition.ConstEffect.GetValue() + gun.MunitionHitEffect, _ = munition.MunitionHitEffect.GetValue() + + if hull_damange, ok := munition.HullDamage.GetValue(); ok { + // regular gun or turret + gun.HullDamage = hull_damange + gun.EnergyDamage = munition.EnergyDamange.Get() + } else { + + if explosion_arch, ok := munition.ExplosionArch.GetValue(); ok { + // rocket launcher + explosion := e.Configs.Equip.ExplosionMap[explosion_arch] + gun.HullDamage = explosion.HullDamage.Get() + gun.EnergyDamage = explosion.EnergyDamange.Get() + } else { + // healing gun + gun.HullDamage = -1 + } + + } + + if required_ammo, ok := munition.RequiredAmmo.GetValue(); ok { + gun.RequiredAmmo = required_ammo + } + + if value, ok := munition.AmmoLimitAmountInCatridge.GetValue(); ok { + gun.AmmoLimit.AmountInCatridge = ptr.Ptr(value) + } + if value, ok := munition.AmmoLimitMaxCatridges.GetValue(); ok { + gun.AmmoLimit.MaxCatridges = ptr.Ptr(value) + } + + gun.DamageType = "undefined" + if weapon_type, ok := munition.WeaponType.GetValue(); ok { + gun.DamageType = weapon_type + } + + if lifetime, ok := munition.LifeTime.GetValue(); ok { + gun.LifeTime = lifetime + } else { + gun.LifeTime = 100000000 + } + gun.Range = gun.LifeTime * gun.Speed + + if weapon_type, ok := e.Configs.WeaponMods.WeaponTypesMap[gun.DamageType]; ok { + for _, weapon_modifier := range weapon_type.ShieldMods { + gun.DamageBonuses = append(gun.DamageBonuses, + DamageBonus{ + Type: weapon_modifier.ShieldType.Get(), + Modifier: weapon_modifier.DamageModifier.Get(), + }, + ) + } + } + + gun.Price = -1 + if good_info, ok := e.Configs.Goods.GoodsMap[gun.Nickname]; ok { + if price, ok := good_info.Price.GetValue(); ok { + gun.Price = price + gun.Bases = e.GetAtBasesSold(GetCommodityAtBasesInput{ + Nickname: good_info.Nickname.Get(), + Price: price, + }) + } + } + + gun.Name = e.GetInfocardName(gun.IdsName, gun.Nickname) + + if NameWithSpacesOnly(gun.Name) { + gun.Name = "Undefined" + } + + e.exportInfocards(InfocardKey(gun.Nickname), gun.IdsInfo) + + gun.ShieldDamage = int(float64(gun.HullDamage)*float64(e.Configs.Consts.ShieldEquipConsts.HULL_DAMAGE_FACTOR.Get()) + float64(gun.EnergyDamage)) + + avg_shield_modifier := 0.0 + shield_modifier_count := 0 + for _, damage_bonus := range gun.DamageBonuses { + if _, ok := buyable_ship_tech[damage_bonus.Type]; !ok { + continue + } + avg_shield_modifier += damage_bonus.Modifier + shield_modifier_count += 1 + } + if shield_modifier_count == 0 { + shield_modifier_count = 1 + avg_shield_modifier = 1 + } + + avgShieldModifier := avg_shield_modifier / float64(shield_modifier_count) + gun.AvgShieldDamage = int(float64(gun.ShieldDamage) * avgShieldModifier) + + gun.HullDamagePerSec = float64(gun.HullDamage) * gun.Refire * float64(num_barrels) + gun.EnergyDamagePerSec = float64(gun.EnergyDamage) * gun.Refire * float64(num_barrels) + gun.AvgShieldDamagePerSec = float64(gun.AvgShieldDamage) * gun.Refire * float64(num_barrels) + + gun.PowerUsagePerSec = float64(gun.PowerUsage) * gun.Refire * float64(num_barrels) + + if gun.BurstFire != nil { + gun.BurstFire.SustainedHullDamagePerSec = float64(gun.HullDamage) * gun.BurstFire.SustainedRefire * float64(num_barrels) + gun.BurstFire.SustainedEnergyDamagePerSec = float64(gun.EnergyDamage) * gun.BurstFire.SustainedRefire * float64(num_barrels) + gun.BurstFire.SustainedAvgShieldDamagePerSec = float64(gun.AvgShieldDamage) * gun.BurstFire.SustainedRefire * float64(num_barrels) + gun.BurstFire.SustainedPowerUsagePerSec = float64(gun.PowerUsage) * gun.BurstFire.SustainedRefire * float64(num_barrels) + } + + power_usage_for_calcs := gun.PowerUsage + if power_usage_for_calcs == 0 { + power_usage_for_calcs = 1 + } + gun.AvgEfficiency = (float64(gun.HullDamage) + float64(gun.AvgShieldDamage)) / (power_usage_for_calcs * 2) + gun.HullEfficiency = float64(gun.HullDamage) / power_usage_for_calcs + gun.ShieldEfficiency = float64(gun.AvgShieldDamage) / power_usage_for_calcs + gun.EnergyDamageEfficiency = float64(gun.EnergyDamage) / power_usage_for_calcs + + gun.GunTurnRate, _ = gun_info.TurnRate.GetValue() + gun.DispersionAngle, _ = gun_info.DispersionAngle.GetValue() + + if gun.IsAutoTurret { + gun.Type = "turret" + } else { + gun.Type = "gun" + } + + // fmt.Println("CalculateTEchCompat", e.configs.Discovery != nil, gun.Nickname) + gun.DiscoveryTechCompat = CalculateTechCompat(e.Configs.Discovery, ids, gun.Nickname) + + if e.Configs.Discovery != nil { + gun.DiscoGun = &DiscoGun{} + if armor_pen, ok := munition.ArmorPen.GetValue(); ok { + gun.DiscoGun.ArmorPen = armor_pen + } + + if explosion_arch, ok := munition.ExplosionArch.GetValue(); ok { + // rocket launcher + explosion := e.Configs.Equip.ExplosionMap[explosion_arch] + if armor_pen, ok := explosion.ArmorPen.GetValue(); ok { + gun.DiscoGun.ArmorPen = armor_pen + } + } + } + + e.Hashes[gun.Nickname] = gun.NicknameHash + e.Hashes[munition.Nickname.Get()] = gun.MunitionHash + e.Hashes[gun.HpType] = gun.HpTypeHash + + return gun, nil +} + +func (e *Exporter) GetBuyableShields(shields []Shield) map[string]bool { + var buyable_ship_tech map[string]bool = make(map[string]bool) + for _, shield := range shields { + if !e.Buyable(shield.Bases) { + continue + } + buyable_ship_tech[shield.Technology] = true + } + return buyable_ship_tech +} + +func (e *Exporter) GetGuns(ids []*Tractor, buyable_ship_tech map[string]bool) []Gun { + var guns []Gun + + for _, gun_info := range e.Configs.Equip.Guns { + gun, err := e.getGunInfo(gun_info, ids, buyable_ship_tech) + + if err != nil { + continue + } + + munition := e.Configs.Equip.MunitionMap[gun_info.ProjectileArchetype.Get()] + if _, ok := munition.Motor.GetValue(); ok { + // Excluded rocket launching stuff + continue + } + + guns = append(guns, gun) + } + + return guns +} + +func (e *Exporter) FilterToUsefulGun(guns []Gun) []Gun { + var items []Gun = make([]Gun, 0, len(guns)) + for _, gun := range guns { + + if gun.HpType == "" { + continue + } + + if strings.Contains(gun.DamageType, "w_npc") || strings.Contains(gun.DamageType, "station") { + continue + } + if strings.Contains(gun.Name, "Obsolete Equipment") { + continue + } + if strings.Contains(gun.Nickname, "_wp_") || + strings.Contains(gun.Nickname, "_wps_") || + strings.Contains(gun.Nickname, "_station_") || + strings.Contains(gun.Nickname, "admin_cannon") { + continue + } + + if !e.Buyable(gun.Bases) { + continue + } + items = append(items, gun) + } + return items +} diff --git a/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/lootable.go b/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/lootable.go new file mode 100644 index 0000000..1c710c7 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/lootable.go @@ -0,0 +1,82 @@ +package configs_export + +import "github.com/darklab8/fl-darkstat/configs/cfg" + +func (e *Exporter) findable_in_loot() map[string]bool { + if e.findable_in_loot_cache != nil { + return e.findable_in_loot_cache + } + + e.findable_in_loot_cache = make(map[string]bool) + + for _, system := range e.Configs.Systems.Systems { + for _, wreck := range system.Wrecks { + louadout_nickname := wreck.Loadout.Get() + if loadout, ok := e.Configs.Loadouts.LoadoutsByNick[louadout_nickname]; ok { + for _, cargo := range loadout.Cargos { + e.findable_in_loot_cache[cargo.Nickname.Get()] = true + } + } + } + } + + for _, npc_arch := range e.Configs.NpcShips.NpcShips { + loadout_nickname := npc_arch.Loadout.Get() + if loadout, ok := e.Configs.Loadouts.LoadoutsByNick[loadout_nickname]; ok { + for _, cargo := range loadout.Cargos { + e.findable_in_loot_cache[cargo.Nickname.Get()] = true + } + } + } + return e.findable_in_loot_cache +} + +/* +It fixes issue of Guns obtainable only via wrecks being invisible +*/ +const ( + BaseLootableName = "Lootable" + BaseLootableFaction = "Wrecks and Missions" + BaseLootableNickname = "base_loots" +) + +func (e *Exporter) EnhanceBasesWithLoot(bases []*Base) []*Base { + + in_wrecks := e.findable_in_loot() + + base := &Base{ + Name: "Lootable", + MarketGoodsPerNick: make(map[CommodityKey]*MarketGood), + Nickname: cfg.BaseUniNick(BaseLootableNickname), + InfocardKey: InfocardKey(BaseLootableNickname), + SystemNickname: "neverwhere", + System: "Neverwhere", + Region: "Neverwhere", + FactionName: BaseLootableFaction, + } + + base.Archetypes = append(base.Archetypes, BaseLootableNickname) + + for wreck, _ := range in_wrecks { + market_good := &MarketGood{ + GoodInfo: e.GetGoodInfo(wreck), + BaseSells: true, + ShipClass: -1, + IsServerSideOverride: true, + } + e.Hashes[market_good.Nickname] = market_good.NicknameHash + + market_good_key := GetCommodityKey(market_good.Nickname, market_good.ShipClass) + base.MarketGoodsPerNick[market_good_key] = market_good + } + + var sb []InfocardLine + sb = append(sb, NewInfocardSimpleLine(base.Name)) + sb = append(sb, NewInfocardSimpleLine(`This is only pseudo base to show availability of lootable content`)) + sb = append(sb, NewInfocardSimpleLine(`The content is findable in wrecks or drops from ships at missions`)) + + e.Infocards[InfocardKey(base.Nickname)] = sb + + bases = append(bases, base) + return bases +} diff --git a/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/mines.go b/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/mines.go new file mode 100644 index 0000000..16cf5da --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/mines.go @@ -0,0 +1,149 @@ +package configs_export + +import ( + "math" + + "github.com/darklab8/fl-darkstat/configs/cfg" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/initialworld/flhash" + "github.com/darklab8/go-utils/utils/ptr" +) + +type Mine struct { + Name string `json:"name"` + Price int `json:"price"` + AmmoPrice int `json:"ammo_price"` + Nickname string `json:"nickname"` + MineDropperHash flhash.HashCode `json:"mine_dropper_hash" format:"int64"` + ProjectileArchetype string `json:"projectyle_archetype"` + MineHash flhash.HashCode `json:"mine_hash" format:"int64"` + IdsName int `json:"ids_name"` + IdsInfo int `json:"ids_info"` + + HullDamage int `json:"hull_damage"` + EnergyDamange int `json:"energy_damage"` + ShieldDamage int `json:"shield_damage"` + PowerUsage float64 `json:"power_usage"` + + Value float64 `json:"value"` + Refire float64 `json:"refire"` + DetonationDistance float64 `json:"detonation_distance"` + Radius float64 `json:"radius"` + SeekDistance int `json:"seek_distance"` + TopSpeed int `json:"top_speed"` + Acceleration int `json:"acceleration"` + LinearDrag float64 `json:"linear_drag"` + LifeTime float64 `json:"life_time"` + OwnerSafe int `json:"owner_safe"` + Toughness float64 `json:"toughness"` + + HitPts int `json:"hit_pts"` + Lootable bool `json:"lootable"` + + Bases map[cfg.BaseUniNick]*MarketGood `json:"-" swaggerignore:"true"` + + *DiscoveryTechCompat `json:"-" swaggerignore:"true"` + + AmmoLimit AmmoLimit `json:"ammo_limit"` + Mass float64 `json:"mass"` +} + +func (b Mine) GetNickname() string { return string(b.Nickname) } + +func (b Mine) GetBases() map[cfg.BaseUniNick]*MarketGood { return b.Bases } + +func (b Mine) GetDiscoveryTechCompat() *DiscoveryTechCompat { return b.DiscoveryTechCompat } + +type AmmoLimit struct { + // Disco stuff + AmountInCatridge *int + MaxCatridges *int +} + +func (e *Exporter) GetMines(ids []*Tractor) []Mine { + var mines []Mine + + for _, mine_dropper := range e.Configs.Equip.MineDroppers { + mine := Mine{ + Bases: make(map[cfg.BaseUniNick]*MarketGood), + } + mine.Mass, _ = mine_dropper.Mass.GetValue() + + mine.Nickname = mine_dropper.Nickname.Get() + mine.MineDropperHash = flhash.HashNickname(mine.Nickname) + e.Hashes[mine.Nickname] = mine.MineDropperHash + + mine.IdsInfo = mine_dropper.IdsInfo.Get() + mine.IdsName = mine_dropper.IdsName.Get() + mine.PowerUsage = mine_dropper.PowerUsage.Get() + mine.Lootable = mine_dropper.Lootable.Get() + mine.Toughness = mine_dropper.Toughness.Get() + mine.HitPts = mine_dropper.HitPts.Get() + + if good_info, ok := e.Configs.Goods.GoodsMap[mine.Nickname]; ok { + if price, ok := good_info.Price.GetValue(); ok { + mine.Price = price + mine.Bases = e.GetAtBasesSold(GetCommodityAtBasesInput{ + Nickname: good_info.Nickname.Get(), + Price: price, + }) + } + } + + mine.Name = e.GetInfocardName(mine.IdsName, mine.Nickname) + + mine_info := e.Configs.Equip.MinesMap[mine_dropper.ProjectileArchetype.Get()] + mine.ProjectileArchetype = mine_info.Nickname.Get() + mine.MineHash = flhash.HashNickname(mine.ProjectileArchetype) + e.Hashes[mine.ProjectileArchetype] = mine.MineHash + + explosion := e.Configs.Equip.ExplosionMap[mine_info.ExplosionArch.Get()] + + mine.HullDamage = explosion.HullDamage.Get() + mine.EnergyDamange = explosion.EnergyDamange.Get() + mine.ShieldDamage = int(float64(mine.HullDamage)*float64(e.Configs.Consts.ShieldEquipConsts.HULL_DAMAGE_FACTOR.Get()) + float64(mine.EnergyDamange)) + + mine.Radius = float64(explosion.Radius.Get()) + + mine.Refire = float64(1 / mine_dropper.RefireDelay.Get()) + + mine.DetonationDistance = float64(mine_info.DetonationDistance.Get()) + mine.OwnerSafe = mine_info.OwnerSafeTime.Get() + mine.SeekDistance = mine_info.SeekDist.Get() + mine.TopSpeed = mine_info.TopSpeed.Get() + mine.Acceleration = mine_info.Acceleration.Get() + mine.LifeTime = mine_info.Lifetime.Get() + mine.LinearDrag = mine_info.LinearDrag.Get() + + if mine_good_info, ok := e.Configs.Goods.GoodsMap[mine_info.Nickname.Get()]; ok { + if price, ok := mine_good_info.Price.GetValue(); ok { + mine.AmmoPrice = price + mine.Value = math.Max(float64(mine.HullDamage), float64(mine.ShieldDamage)) / float64(mine.AmmoPrice) + } + } + + if value, ok := mine_info.AmmoLimitAmountInCatridge.GetValue(); ok { + mine.AmmoLimit.AmountInCatridge = ptr.Ptr(value) + } + if value, ok := mine_info.AmmoLimitMaxCatridges.GetValue(); ok { + mine.AmmoLimit.MaxCatridges = ptr.Ptr(value) + } + + e.exportInfocards(InfocardKey(mine.Nickname), mine.IdsInfo) + mine.DiscoveryTechCompat = CalculateTechCompat(e.Configs.Discovery, ids, mine.Nickname) + + mines = append(mines, mine) + } + + return mines +} + +func (e *Exporter) FilterToUsefulMines(mines []Mine) []Mine { + var items []Mine = make([]Mine, 0, len(mines)) + for _, item := range mines { + if !e.Buyable(item.Bases) { + continue + } + items = append(items, item) + } + return items +} diff --git a/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/missions.go b/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/missions.go new file mode 100644 index 0000000..ecae2a5 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/missions.go @@ -0,0 +1,385 @@ +package configs_export + +import ( + "sort" + "strconv" + + "github.com/darklab8/fl-darkstat/configs/cfg" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/universe_mapped" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/universe_mapped/systems_mapped" +) + +type EnemyFaction struct { + Faction + NpcExist bool +} + +/* +Calculates for enemy faction percentage of ships defined in faction_props/npcships.ini +If they aren't defined, Freelancer will be showing corrupted no missions when they encounter. +*/ +func (e *Exporter) NewEnemyFaction(faction Faction, npc_ranks []int) EnemyFaction { + var npc_ranks_need map[int]bool = make(map[int]bool) + for _, rank := range npc_ranks { + npc_ranks_need[rank] = true + } + + var npc_ranks_exist map[int]bool = make(map[int]bool) + + result := EnemyFaction{ + Faction: faction, + } + + faction_prop, prop_exists := e.Configs.FactionProps.FactionPropMapByNickname[faction.Nickname] + + if !prop_exists { + return result + } + + for _, npc_ship := range faction_prop.NpcShips { + npc_ship_nickname := npc_ship.Get() + if npc_shiparch, ok := e.Configs.NpcShips.NpcShipsByNickname[npc_ship_nickname]; ok { + + has_class_fighter := false + for _, npc_class := range npc_shiparch.NpcClass { + if npc_class.Get() == "class_fighter" { + has_class_fighter = true + break + } + } + if !has_class_fighter { + continue + } + str_level := npc_shiparch.Level.Get() + if level, err := strconv.Atoi(str_level[1:]); err == nil { + + if _, ok := npc_ranks_need[level]; ok { + npc_ranks_exist[level] = true + } + } + } + } + + result.NpcExist = len(npc_ranks_exist) > 0 + return result +} + +type MissioNFaction struct { + FactionName string + FactionNickname string + MinDifficulty float64 + MaxDifficulty float64 + Weight int + Infocard InfocardKey + + MinAward int + MaxAward int + NpcRanks []int + Enemies []EnemyFaction + Err cfg.Err +} + +type BaseMissions struct { + MinOffers int + MaxOffers int + Factions []MissioNFaction + NpcRanksAtBaseMap map[int]bool + NpcRanksAtBase []int + + EnemiesAtBaseMap map[string]EnemyFaction + + MinMoneyAward int + MaxMoneyAward int + Vignettes int + Err cfg.Err +} + +type DiffToMoney struct { + MinLevel float64 + MoneyAward int +} + +func (e *Exporter) GetMissions(bases []*Base, factions []Faction) []*Base { + + var factions_map map[string]Faction = make(map[string]Faction) + for _, faction := range factions { + factions_map[faction.Nickname] = faction + } + + var diffs_to_money []DiffToMoney + for _, diff_to_money := range e.Configs.DiffToMoney.DiffToMoney { + diffs_to_money = append(diffs_to_money, DiffToMoney{ + MinLevel: diff_to_money.MinLevel.Get(), + MoneyAward: diff_to_money.MoneyAward.Get(), + }) + } + sort.Slice(diffs_to_money, func(i, j int) bool { + return diffs_to_money[i].MinLevel > diffs_to_money[j].MinLevel + }) + + for base_index, base := range bases { + base.Missions.NpcRanksAtBaseMap = make(map[int]bool) + base.Missions.EnemiesAtBaseMap = make(map[string]EnemyFaction) + + base_info, ok := e.Configs.MBases.BaseMap[base.Nickname] + if !ok { + base.Missions.Err = cfg.NewErr("base is not defined in mbases") + bases[base_index] = base + continue + } + + if universe_base, ok := e.Configs.Universe.BasesMap[universe_mapped.BaseNickname(base.Nickname)]; ok { + + _, bar_exists := universe_base.ConfigBase.RoomMapByRoomNickname["bar"] + if !bar_exists { + base.Missions.Err = cfg.NewErr("bar is not defined for the base") + bases[base_index] = base + continue + } + } + + // Firstly finding SystemBase coresponding to base + system, system_exists := e.Configs.Systems.SystemsMap[base.SystemNickname] + if !system_exists { + base.Missions.Err = cfg.NewErr("system is not found for base") + bases[base_index] = base + continue + } + + var system_base *systems_mapped.Base + for _, sys_base := range system.Bases { + if sys_base.IdsName.Get() == base.StridName { + system_base = sys_base + break + } + } + if system_base == nil { + base.Missions.Err = cfg.NewErr("base is not found in system") + bases[base_index] = base + continue + } + + // Verify that base vignette fields exist in 30k around of it, otherwise base is not able to start missions + + vignette_valid_base_mission_range := float64(30000) + for _, vignette := range system.MissionZoneVignettes { + distance, dist_err := DistanceForVecs(system_base.Pos, vignette.Pos) + if dist_err != nil { + continue + } + + if distance < vignette_valid_base_mission_range+float64(vignette.Size.Get()) { + base.Missions.Vignettes += 1 + + } + } + + if base.Missions.Vignettes == 0 { + base.Missions.Err = cfg.NewErr("base has no vignette zones") + bases[base_index] = base + continue + } + + if base_info.MVendor == nil { + base.Missions.Err = cfg.NewErr("no mvendor in mbase") + bases[base_index] = base + continue + } + + for _, faction_info := range base_info.BaseFactions { + faction := MissioNFaction{ + FactionNickname: faction_info.Faction.Get(), + } + faction.MinDifficulty, _ = faction_info.MissionType.MinDifficulty.GetValue() + faction.MaxDifficulty, _ = faction_info.MissionType.MaxDifficulty.GetValue() + + if value, ok := faction_info.Weight.GetValue(); ok { + faction.Weight = value + } else { + faction.Weight = 100 + } + + faction_export_info, faction_exists := factions_map[faction.FactionNickname] + if !faction_exists { + faction.Err = cfg.NewErr("mission faction does not eixst") + base.Missions.Factions = append(base.Missions.Factions, faction) + continue + } + + faction.Infocard = faction_export_info.InfocardKey + faction.FactionName = faction_export_info.Name + + _, gives_missions := faction_info.MissionType.MinDifficulty.GetValue() + if !gives_missions { + faction.Err = cfg.NewErr("mission_type is not in mbase") + base.Missions.Factions = append(base.Missions.Factions, faction) + continue + } + + for money_index, diff_to_money := range diffs_to_money { + + if money_index == 0 { + continue + } + + if faction.MinDifficulty >= diff_to_money.MinLevel { + diff_range := diffs_to_money[money_index-1].MinLevel - diff_to_money.MinLevel + bonus_range := faction.MinDifficulty - diff_to_money.MinLevel + bonus_money_percentage := bonus_range / diff_range + bonus_money := int(float64(diffs_to_money[money_index-1].MoneyAward-diff_to_money.MoneyAward) * bonus_money_percentage) + faction.MinAward = diff_to_money.MoneyAward + bonus_money + } + + if faction.MaxDifficulty >= diff_to_money.MinLevel && faction.MaxAward == 0 { + diff_range := diffs_to_money[money_index-1].MinLevel - diff_to_money.MinLevel + bonus_range := faction.MaxDifficulty - diff_to_money.MinLevel + bonus_money_percentage := bonus_range / diff_range + bonus_money := int(float64(diffs_to_money[money_index-1].MoneyAward-diff_to_money.MoneyAward) * bonus_money_percentage) + faction.MaxAward = diff_to_money.MoneyAward + bonus_money + } + } + + // NpcRank appropriate to current mission difficulty based on set range. + for _, rank_to_diff := range e.Configs.NpcRankToDiff.NPCRankToDifficulties { + + min_diff := rank_to_diff.Difficulties[0].Get() + max_diff := rank_to_diff.Difficulties[len(rank_to_diff.Difficulties)-1].Get() + + if faction.MinDifficulty >= min_diff && faction.MinDifficulty <= max_diff { + faction.NpcRanks = append(faction.NpcRanks, rank_to_diff.Rank.Get()) + continue + } + if faction.MaxDifficulty >= min_diff && faction.MaxDifficulty <= max_diff { + faction.NpcRanks = append(faction.NpcRanks, rank_to_diff.Rank.Get()) + continue + } + } + + // Find if enemy npc spawn zones are intersecting with Vignettes. + // They will be all the enemies for the faction. + var target_reputation_by_faction map[string]Reputation = make(map[string]Reputation) + for _, reputation := range faction_export_info.Reputations { + target_reputation_by_faction[reputation.Nickname] = reputation + } + var base_enemies map[string]Faction = make(map[string]Faction) + for _, npc_spawn_zone := range system.MissionsSpawnZone { + + var enemies []*systems_mapped.Patrol = make([]*systems_mapped.Patrol, 0, len(npc_spawn_zone.Factions)) + for _, potential_enemy := range npc_spawn_zone.Factions { + potential_enemy_nickname, _ := potential_enemy.FactionNickname.GetValue() + potential_enemy_rep, rep_exists := target_reputation_by_faction[potential_enemy_nickname] + if !rep_exists { + continue + } + + if potential_enemy_rep.Rep <= -(0.3 - 0.001) { + enemies = append(enemies, potential_enemy) + } + } + + if len(enemies) == 0 { + continue + } + + // EXPERIMENTAL: Turned off check for vignette check to be within npc spawning zones. + // looks to be not necessary + // if !IsAnyVignetteWithinNPCSpawnRange(system, npc_spawn_zone) { + // continue + // } + + for _, enemy := range enemies { + faction_enemy, faction_found := factions_map[enemy.FactionNickname.Get()] + if !faction_found { + continue + } + copy_enemy := faction_enemy + base_enemies[faction_enemy.Nickname] = copy_enemy + } + } + + if len(base_enemies) == 0 { + faction.Err = cfg.NewErr("no npc spawn zones with enemies") + base.Missions.Factions = append(base.Missions.Factions, faction) + continue + } + for _, enemy_faction := range base_enemies { + faction.Enemies = append(faction.Enemies, e.NewEnemyFaction(enemy_faction, faction.NpcRanks)) + } + + base.Missions.Factions = append(base.Missions.Factions, faction) + } + + // Make sanity check that Factions were added to base + // If not then don't add to it mission existence. + if len(base.Missions.Factions) == 0 { + base.Missions.Err = cfg.NewErr("no msn giving factions found") + bases[base_index] = base + continue + } + + npc_exist := false + for _, faction := range base.Missions.Factions { + if faction.Err != nil { + continue + } + for _, enemy_faction := range faction.Enemies { + if enemy_faction.NpcExist { + npc_exist = true + } + } + } + if !npc_exist { + base.Missions.Err = cfg.NewErr("npcs do not exist") + bases[base_index] = base + continue + } + + if base_info.MVendor != nil { + base.Missions.MinOffers, _ = base_info.MVendor.MinOffers.GetValue() + base.Missions.MaxOffers, _ = base_info.MVendor.MaxOffers.GetValue() + + if base.Missions.Vignettes < base.Missions.MinOffers { + base.Missions.MinOffers = base.Missions.Vignettes + } + if base.Missions.Vignettes < base.Missions.MaxOffers { + base.Missions.MaxOffers = base.Missions.Vignettes + } + } + + // summarization for base + for fc_index, faction := range base.Missions.Factions { + if faction.Err != nil { + faction.MaxAward = 0 + faction.MinAward = 0 + base.Missions.Factions[fc_index] = faction + continue + } + + for _, npc_rank := range faction.NpcRanks { + base.Missions.NpcRanksAtBaseMap[npc_rank] = true + } + + for _, enemy_faction := range faction.Enemies { + base.Missions.EnemiesAtBaseMap[enemy_faction.Nickname] = enemy_faction + } + + if faction.MinAward < base.Missions.MinMoneyAward || base.Missions.MinMoneyAward == 0 { + base.Missions.MinMoneyAward = faction.MinAward + } + + if faction.MaxAward > base.Missions.MaxMoneyAward { + base.Missions.MaxMoneyAward = faction.MaxAward + } + } + + // add unique found ship categories from factions to Missions overview + for key := range base.Missions.NpcRanksAtBaseMap { + base.Missions.NpcRanksAtBase = append(base.Missions.NpcRanksAtBase, key) + } + sort.Ints(base.Missions.NpcRanksAtBase) + + bases[base_index] = base + } + + return bases +} diff --git a/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/missions_utils.go b/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/missions_utils.go new file mode 100644 index 0000000..96102d3 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/missions_utils.go @@ -0,0 +1,70 @@ +package configs_export + +import ( + "errors" + "math" + + "github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/universe_mapped/systems_mapped" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/semantic" + "github.com/darklab8/fl-darkstat/configs/configs_settings/logus" +) + +func DistanceForVecs(Pos1 *semantic.Vect, Pos2 *semantic.Vect) (float64, error) { + if _, ok := Pos1.X.GetValue(); !ok { + return 0, errors.New("no x") + } + if _, ok := Pos2.X.GetValue(); !ok { + return 0, errors.New("no x") + } + + x_dist := math.Pow((Pos1.X.Get() - Pos2.X.Get()), 2) + y_dist := math.Pow((Pos1.Y.Get() - Pos2.Y.Get()), 2) + z_dist := math.Pow((Pos1.Z.Get() - Pos2.Z.Get()), 2) + distance := math.Pow((x_dist + y_dist + z_dist), 0.5) + return distance, nil +} + +func GetMaxRadius(Size *semantic.Vect) (float64, error) { + max_size := 0.0 + if value, ok := Size.X.GetValue(); ok { + if value > max_size { + max_size = value + } + } + if value, ok := Size.Y.GetValue(); ok { + if value > max_size { + max_size = value + } + } + if value, ok := Size.Z.GetValue(); ok { + if value > max_size { + max_size = value + } + } + if max_size == 0 { + return 0, errors.New("not found size") + } + + return max_size, nil +} + +func IsAnyVignetteWithinNPCSpawnRange(system *systems_mapped.System, npc_spawn_zone *systems_mapped.MissionPatrolZone) bool { + matched_vignette := false + for _, vignette := range system.MissionZoneVignettes { + + distance, dist_err := DistanceForVecs(vignette.Pos, npc_spawn_zone.Pos) + if dist_err != nil { + continue + } + + max_spwn_zone_size, err_max_size := GetMaxRadius(npc_spawn_zone.Size) + logus.Log.CheckWarn(err_max_size, "expected finding max size, but object does not have it") + + if distance < float64(vignette.Size.Get())+max_spwn_zone_size { + matched_vignette = true + break + } + } + + return matched_vignette +} diff --git a/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/missles.go b/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/missles.go new file mode 100644 index 0000000..7fbbe4b --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/missles.go @@ -0,0 +1,33 @@ +package configs_export + +type Missile struct { + MaxAngularVelocity float64 `json:"max_angular_velocity"` +} + +func (e *Exporter) GetMissiles(ids []*Tractor, buyable_ship_tech map[string]bool) []Gun { + var missiles []Gun + + for _, gun_info := range e.Configs.Equip.Guns { + missile, err := e.getGunInfo(gun_info, ids, buyable_ship_tech) + + if err != nil { + continue + } + + if missile.HpType == "" { + continue + } + + munition := e.Configs.Equip.MunitionMap[gun_info.ProjectileArchetype.Get()] + if _, ok := munition.Motor.GetValue(); !ok { + // Excluded regular guns + continue + } + + missile.MaxAngularVelocity, _ = munition.MaxAngularVelocity.GetValue() + + missiles = append(missiles, missile) + } + + return missiles +} diff --git a/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/ores.go b/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/ores.go new file mode 100644 index 0000000..0fc7273 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/ores.go @@ -0,0 +1,257 @@ +package configs_export + +import ( + "fmt" + "strings" + + "github.com/darklab8/fl-darkstat/configs/cfg" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/initialworld/flhash" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/universe_mapped" + "github.com/darklab8/fl-darkstat/configs/configs_settings/logus" + "github.com/darklab8/go-typelog/typelog" + "github.com/darklab8/go-utils/utils/ptr" +) + +type MiningInfo struct { + DynamicLootMin int + DynamicLootMax int + DynamicLootDifficulty int + MinedGood *MarketGood +} + +func (e *Exporter) GetOres(Commodities []*Commodity) []*Base { + var bases []*Base + + var comm_by_nick map[CommodityKey]*Commodity = make(map[CommodityKey]*Commodity) + for _, comm := range Commodities { + comm_by_nick[GetCommodityKey(comm.Nickname, comm.ShipClass)] = comm + } + + for _, system := range e.Configs.Systems.Systems { + + system_uni, system_uni_ok := e.Configs.Universe.SystemMap[universe_mapped.SystemNickname(system.Nickname)] + + for _, asteroids := range system.Asteroids { + + asteroid_zone_nick := asteroids.Zone.Get() + + zone, found_zone := system.ZonesByNick[asteroid_zone_nick] + if !found_zone { + continue + } + + if asteroids.LootableZone == nil { + continue + } + + commodity, commodity_found := asteroids.LootableZone.AsteroidLootCommodity.GetValue() + + if !commodity_found { + continue + } + + location := zone.Pos.Get() + var added_goods map[string]bool = make(map[string]bool) + base := &Base{ + MiningInfo: &MiningInfo{}, + Pos: location, + MarketGoodsPerNick: make(map[CommodityKey]*MarketGood), + } + base.DynamicLootMin, _ = asteroids.LootableZone.DynamicLootMin.GetValue() + base.DynamicLootMax, _ = asteroids.LootableZone.DynamicLootMax.GetValue() + base.DynamicLootDifficulty, _ = asteroids.LootableZone.DynamicLootDifficulty.GetValue() + + var base_nickname string + base_nickname, _ = zone.Nickname.GetValue() + base.Nickname = cfg.BaseUniNick(base_nickname) + + base.NicknameHash = flhash.HashNickname(base_nickname) + e.Hashes[base_nickname] = base.NicknameHash + + base.InfocardID, _ = zone.IDsInfo.GetValue() + base.StridName, _ = zone.IdsName.GetValue() + + base.InfocardKey = InfocardKey(base.Nickname) + + base.Archetypes = append(base.Archetypes, "mining_operation") + base.FactionName = "Mining Field" + + base.SystemNickname = system.Nickname + base.SystemNicknameHash = flhash.HashNickname(base.SystemNickname) + if system, ok := e.Configs.Universe.SystemMap[universe_mapped.SystemNickname(base.SystemNickname)]; ok { + base.System = e.GetInfocardName(system.StridName.Get(), base.SystemNickname) + base.Region = e.GetRegionName(system) + base.SectorCoord = VectorToSectorCoord(system_uni, base.Pos) + } + + logus.Log.Debug("GetOres", typelog.String("commodity=", commodity)) + + equipment := e.Configs.Equip.CommoditiesMap[commodity] + for _, volume_info := range equipment.Volumes { + + market_good := &MarketGood{ + GoodInfo: e.GetGoodInfo(commodity), + BaseSells: true, + PriceBaseSellsFor: 0, + PriceBaseBuysFor: nil, + Volume: volume_info.Volume.Get(), + ShipClass: volume_info.GetShipClass(), + BaseInfo: BaseInfo{ + BaseNickname: base.Nickname, + BaseName: base.Name, + SystemName: base.System, + BasePos: base.Pos, + Region: base.Region, + FactionName: "Mining Field", + SectorCoord: base.SectorCoord, + }, + } + base.Name = market_good.Name + market_good.BaseName = market_good.Name + + market_good_key := GetCommodityKey(market_good.Nickname, market_good.ShipClass) + base.MarketGoodsPerNick[market_good_key] = market_good + base.MinedGood = market_good + added_goods[market_good.Nickname] = true + + if commodity, ok := comm_by_nick[market_good_key]; ok { + commodity.Bases[market_good.BaseNickname] = market_good + } + + } + + if e.Configs.Discovery != nil { + if recipes, ok := e.Configs.Discovery.BaseRecipeItems.RecipePerConsumed[commodity]; ok { + for _, recipe := range recipes { + recipe_produces_only_commodities := true + + for _, produced := range recipe.ProcucedItem { + + _, is_commodity := e.Configs.Equip.CommoditiesMap[produced.Get()] + if !is_commodity { + recipe_produces_only_commodities = false + break + } + + } + + if recipe_produces_only_commodities { + for _, produced := range recipe.ProcucedItem { + commodity_produced := produced.Get() + + if _, ok := added_goods[commodity_produced]; ok { + continue + } + equipment := e.Configs.Equip.CommoditiesMap[commodity_produced] + for _, volume_info := range equipment.Volumes { + market_good := &MarketGood{ + GoodInfo: e.GetGoodInfo(commodity_produced), + BaseSells: true, + PriceBaseSellsFor: 0, + PriceBaseBuysFor: nil, + Volume: volume_info.Volume.Get(), + ShipClass: volume_info.GetShipClass(), + BaseInfo: BaseInfo{ + BaseNickname: base.Nickname, + BaseName: base.Name, + SystemName: base.System, + BasePos: base.Pos, + Region: base.Region, + FactionName: "Mining Field", + }, + } + market_good.BaseName = market_good.Name + if system_uni_ok { + market_good.SectorCoord = VectorToSectorCoord(system_uni, market_good.BasePos) + } + market_good_key := GetCommodityKey(market_good.Nickname, market_good.ShipClass) + base.MarketGoodsPerNick[market_good_key] = market_good + if commodity, ok := comm_by_nick[market_good_key]; ok { + commodity.Bases[market_good.BaseNickname] = market_good + } + added_goods[commodity_produced] = true + } + + } + } + } + + } + } + + var sb InfocardBuilder + sb.WriteLineStr(base.Name) + sb.WriteLineStr((`This is is not a base. +It is a mining field with droppable ores`)) + sb.WriteLineStr(("")) + sb.WriteLineStr(("Trade routes shown do not account for a time it takes to mine those ores.")) + + if e.Configs.Discovery != nil { + sb.WriteLineStr("") + sb.WriteLine(InfocardPhrase{Link: ptr.Ptr("https://discoverygc.com/wiki2/Mining"), Phrase: "Check mining tutorial"}, InfocardPhrase{Phrase: " to see how they can be mined"}) + + sb.WriteLineStr("") + sb.WriteLineStr(`NOTE: +for Freelancer Discovery we also add possible sub products of refinery at player bases to possible trade routes from mining field. + `) + } + + sb.WriteLineStr("") + sb.WriteLineStr("commodities:") + for _, good := range base.MarketGoodsPerNick { + if good.Nickname == base.MinedGood.Nickname { + sb.WriteLineStr(fmt.Sprintf("Minable: %s (%s)", good.Name, good.Nickname)) + } else { + sb.WriteLineStr(fmt.Sprintf("Refined at POB: %s (%s)", good.Name, good.Nickname)) + } + } + + e.Infocards[InfocardKey(base.Nickname)] = sb.Lines + + bases = append(bases, base) + + } + _ = system + } + + return bases +} + +var not_useful_ores []string = []string{ + "commodity_water", // sellable + "commodity_oxygen", // sellable + "commodity_scrap_metal", // sellable + "commodity_toxic_waste", // a bit + "commodity_cerulite_crystals", // not + "commodity_alien_organisms", // sellable + "commodity_hydrocarbons", // sellable + "commodity_inert_artifacts", // not + "commodity_organic_capacitors", // not + "commodity_event_ore_01", // not + "commodity_cryo_organisms", // not + "commodity_chirodebris", // not +} + +func FitlerToUsefulOres(bases []*Base) []*Base { + var useful_bases []*Base = make([]*Base, 0, len(bases)) + for _, item := range bases { + if strings.Contains(item.System, "Bastille") { + continue + } + + is_useful := true + for _, useless_commodity := range not_useful_ores { + if item.MinedGood.Nickname == useless_commodity { + is_useful = false + break + } + + } + if !is_useful { + continue + } + + useful_bases = append(useful_bases, item) + } + return useful_bases +} diff --git a/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/pobs.go b/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/pobs.go new file mode 100644 index 0000000..017cbdd --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/pobs.go @@ -0,0 +1,494 @@ +package configs_export + +import ( + "fmt" + "strconv" + "strings" + + "github.com/darklab8/fl-darkstat/configs/cfg" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/equipment_mapped" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/equipment_mapped/equip_mapped" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/initialworld" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/initialworld/flhash" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/universe_mapped" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/infocard_mapped/infocard" + "github.com/darklab8/fl-darkstat/configs/configs_settings/logus" + "github.com/darklab8/fl-darkstat/configs/discovery/pob_goods" + "github.com/darklab8/go-typelog/typelog" + "github.com/darklab8/go-utils/utils/ptr" +) + +type ShopItem struct { + pob_goods.ShopItem + Nickname string `json:"nickname"` + Name string `json:"name"` + Category string `json:"category"` +} + +type DefenseMode int + +func (d DefenseMode) ToStr() string { + switch d { + + case 1: + return "SRP Whitelist > Blacklist > IFF Standing, Anyone with good standing" + case 2: + return "Whitelist > Nodock, Whitelisted ships only" + case 3: + return "Whitelist > Hostile, Whitelisted ships only" + default: + return "not recognized" + } +} + +type PoBWithoutGoods struct { + Nickname string `json:"nickname"` + Name string `json:"name"` + + Pos *string `json:"pos"` + Level *int `json:"level"` + Money *int `json:"money"` + Health *float64 `json:"health"` + DefenseMode *DefenseMode `json:"defense_mode"` + + SystemNick *string `json:"system_nickname"` + SystemName *string `json:"system_name"` // SystemHash *flhash.HashCode `json:"system"` //: 2745655887, + FactionNick *string `json:"faction_nickname"` + FactionName *string `json:"faction_name"` // AffiliationHash *flhash.HashCode `json:"affiliation"` //: 2620, + + ForumThreadUrl *string `json:"forum_thread_url"` + CargoSpaceLeft *int `json:"cargospace"` + + BasePos *cfg.Vector `json:"base_pos"` + SectorCoord *string `json:"sector_coord"` + Region *string `json:"region_name"` +} + +// also known as Player Base Station +type PoB struct { + PoBWithoutGoods + ShopItems []*ShopItem `json:"shop_items"` +} + +func (b PoB) GetNickname() string { return string(b.Nickname) } + +type PoBGood struct { + Nickname string `json:"nickname"` + Name string `json:"name"` + TotalBuyableFromBases int `json:"total_buyable_from_bases"` + TotalSellableToBases int `json:"total_sellable_to_bases"` + + BestPriceToBuy *int `json:"best_price_to_buy"` + BestPriceToSell *int `json:"best_price_to_sell"` + + Category string `json:"category"` + Bases []*PoBGoodBase `json:"bases"` + + AnyBaseSells bool `json:"any_base_sells"` + AnyBaseBuys bool `json:"any_base_buys"` +} + +func (b PoBGood) GetNickname() string { return string(b.Nickname) } + +func (good PoBGood) BaseSells() bool { return good.AnyBaseSells } +func (good PoBGood) BaseBuys() bool { return good.AnyBaseBuys } + +type PoBGoodBase struct { + ShopItem *ShopItem `json:"shop_item"` + Base *PoBWithoutGoods `json:"base"` +} + +// Exporting only with position ones +func (e *Exporter) PoBsToBases(pobs []*PoB) []*Base { + var bases []*Base + + for _, pob := range pobs { + if pob.BasePos == nil { + continue + } + + base := &Base{ + Nickname: cfg.BaseUniNick(pob.Nickname), + NicknameHash: flhash.HashNickname(pob.Nickname), + Name: fmt.Sprintf("(PoB) %s", pob.Name), + Pos: *pob.BasePos, + System: *pob.SystemName, + SystemNickname: *pob.SystemNick, + Region: *pob.Region, + SectorCoord: *pob.SectorCoord, + MarketGoodsPerNick: map[CommodityKey]*MarketGood{}, + IsPob: true, + } + if pob.FactionName != nil { + base.FactionName = *pob.FactionName + } + bases = append(bases, base) + + for _, pob_good := range pob.ShopItems { + market_good := &MarketGood{ + GoodInfo: e.GetGoodInfo(pob_good.Nickname), + IsServerSideOverride: true, + ShipClass: -1, + } + if pob_good.BaseBuys() { + market_good.PriceBaseBuysFor = ptr.Ptr(pob_good.SellPrice) + } + if pob_good.BaseSells() { + market_good.PriceBaseSellsFor = pob_good.Price + market_good.BaseSells = true + } + if market_good.Category == "commodity" { + equipment := e.Configs.Equip.CommoditiesMap[market_good.Nickname] + for _, volume := range equipment.Volumes { + volumed_good := market_good + volumed_good.Volume = volume.Volume.Get() + volumed_good.ShipClass = volume.GetShipClass() + base.MarketGoodsPerNick[GetCommodityKey(volumed_good.Nickname, volumed_good.ShipClass)] = volumed_good + } + } else { + base.MarketGoodsPerNick[GetCommodityKey(market_good.Nickname, market_good.ShipClass)] = market_good + } + } + } + return bases +} + +func (e *Exporter) GetPoBGoods(pobs []*PoB) []*PoBGood { + pobs_goods_by_nick := make(map[string]*PoBGood) + var pob_goods []*PoBGood + + for _, pob := range pobs { + for _, good := range pob.ShopItems { + pob_good, found_good := pobs_goods_by_nick[good.Nickname] + if !found_good { + pob_good = &PoBGood{ + Nickname: good.Nickname, + Name: good.Name, + Category: good.Category, + } + pobs_goods_by_nick[good.Nickname] = pob_good + } + pob_good.Bases = append(pob_good.Bases, &PoBGoodBase{Base: &pob.PoBWithoutGoods, ShopItem: good}) + } + } + + for _, item := range pobs_goods_by_nick { + for _, pob := range item.Bases { + if pob.ShopItem.BaseSells() { + item.AnyBaseSells = true + item.TotalBuyableFromBases += pob.ShopItem.Quantity - pob.ShopItem.MinStock + + if item.BestPriceToBuy == nil { + item.BestPriceToBuy = ptr.Ptr(pob.ShopItem.Price) + } + if pob.ShopItem.Price < *item.BestPriceToBuy { + item.BestPriceToBuy = ptr.Ptr(pob.ShopItem.Price) + } + } + if pob.ShopItem.BaseBuys() { + item.AnyBaseBuys = true + sellable_to_current_base := pob.ShopItem.MaxStock - pob.ShopItem.Quantity + + if pob.Base.CargoSpaceLeft != nil { + if *pob.Base.CargoSpaceLeft < sellable_to_current_base { + sellable_to_current_base = *pob.Base.CargoSpaceLeft + } + } + + item.TotalSellableToBases += sellable_to_current_base + + if item.BestPriceToSell == nil { + item.BestPriceToSell = ptr.Ptr(pob.ShopItem.SellPrice) + } + if pob.ShopItem.SellPrice > *item.BestPriceToSell { + item.BestPriceToSell = ptr.Ptr(pob.ShopItem.SellPrice) + } + } + } + + pob_goods = append(pob_goods, item) + } + + return pob_goods +} + +func (e *Exporter) GetPoBs() []*PoB { + var pobs []*PoB + + systems_by_hash := make(map[flhash.HashCode]*universe_mapped.System) + factions_by_hash := make(map[flhash.HashCode]*initialworld.Group) + for _, system_info := range e.Configs.Universe.Systems { + nickname := system_info.Nickname.Get() + system_hash := flhash.HashNickname(nickname) + systems_by_hash[system_hash] = system_info + } + for _, group_info := range e.Configs.InitialWorld.Groups { + nickname := group_info.Nickname.Get() + group_hash := flhash.HashFaction(nickname) + factions_by_hash[group_hash] = group_info + } + goods_by_hash := make(map[flhash.HashCode]*equip_mapped.Item) + for _, item := range e.Configs.Equip.Items { + nickname := item.Nickname.Get() + hash := flhash.HashNickname(nickname) + goods_by_hash[hash] = item + e.exportInfocards(InfocardKey(nickname), item.IdsInfo.Get()) + } + + ships_by_hash := make(map[flhash.HashCode]*equipment_mapped.Ship) + for _, item := range e.Configs.Goods.Ships { + nickname := item.Nickname.Get() + hash := flhash.HashNickname(nickname) + ships_by_hash[hash] = item + } + + for _, pob_info := range e.Configs.Discovery.PlayerOwnedBases.Bases { + + var pob *PoB = &PoB{ + PoBWithoutGoods: PoBWithoutGoods{ + Nickname: pob_info.Nickname, + Name: pob_info.Name, + Pos: pob_info.Pos, + Level: pob_info.Level, + Money: pob_info.Money, + Health: pob_info.Health, + CargoSpaceLeft: pob_info.CargoSpaceLeft, + }, + } + if pob_info.DefenseMode != nil { + pob.DefenseMode = (*DefenseMode)(pob_info.DefenseMode) + } + if pob_info.Pos != nil { + pob.BasePos = StrPosToVectorPos(*pob_info.Pos) + } + + pob.ForumThreadUrl = pob_info.ForumThreadUrl + if pob_info.SystemHash != nil { + if system, ok := systems_by_hash[*pob_info.SystemHash]; ok { + pob.SystemNick = ptr.Ptr(system.Nickname.Get()) + pob.SystemName = ptr.Ptr(e.GetInfocardName(system.StridName.Get(), system.Nickname.Get())) + + pob.Region = ptr.Ptr(e.GetRegionName(system)) + if pob.BasePos != nil { + pob.SectorCoord = ptr.Ptr(VectorToSectorCoord(system, *pob.BasePos)) + } + } + } + + if pob_info.AffiliationHash != nil { + if faction, ok := factions_by_hash[*pob_info.AffiliationHash]; ok { + pob.FactionNick = ptr.Ptr(faction.Nickname.Get()) + pob.FactionName = ptr.Ptr(e.GetInfocardName(faction.IdsName.Get(), faction.Nickname.Get())) + } + } + + for _, shop_item := range pob_info.ShopItems { + good := &ShopItem{ShopItem: shop_item} + + if item, ok := goods_by_hash[flhash.HashCode(shop_item.Id)]; ok { + good.Nickname = item.Nickname.Get() + good.Name = e.GetInfocardName(item.IdsName.Get(), item.Nickname.Get()) + good.Category = item.Category + } else { + if ship, ok := ships_by_hash[flhash.HashCode(shop_item.Id)]; ok { + ship_hull := e.Configs.Goods.ShipHullsMap[ship.Hull.Get()] + ship_nickname := ship_hull.Ship.Get() + shiparch := e.Configs.Shiparch.ShipsMap[ship_nickname] + good.Nickname = ship_nickname + good.Category = "ship" + good.Name = e.GetInfocardName(shiparch.IdsName.Get(), ship_nickname) + } else { + logus.Log.Warn("unidentified shop item", typelog.Any("shop_item.Id", shop_item.Id)) + } + } + + pob.ShopItems = append(pob.ShopItems, good) + } + + var sb InfocardBuilder + sb.WriteLineStr(pob.Name) + sb.WriteLineStr("") + + if pob_info.Pos == nil && len(pob_info.InfocardParagraphs) == 0 { + sb.WriteLine(InfocardPhrase{Phrase: "infocard:", Bold: true}) + sb.WriteLineStr("no access (toggle pos permission in pob account manager)") + sb.WriteLineStr("") + } + + for _, paragraph := range pob_info.InfocardParagraphs { + sb.WriteLineStr(paragraph) + sb.WriteLineStr("") + } + + if pob_info.DefenseMode != nil { + sb.WriteLine(InfocardPhrase{Phrase: "Defense mode:", Bold: true}) + sb.WriteLineStr((*DefenseMode)(pob_info.DefenseMode).ToStr()) + sb.WriteLineStr("") + } else { + sb.WriteLine(InfocardPhrase{Phrase: "docking permissions:", Bold: true}) + sb.WriteLineStr("no access (toggle defense mode in pob account manager)") + sb.WriteLineStr("") + } + if len(pob_info.SrpFactionHashList) > 0 || len(pob_info.SrpTagList) > 0 || len(pob_info.SrpNameList) > 0 { + sb.WriteLine(InfocardPhrase{Phrase: "Docking allias(srp,ignore rep):", Bold: true}) + sb.WriteLineStr(e.fmt_factions_to_str(factions_by_hash, pob_info.SrpFactionHashList)) + sb.WriteLineStr(fmt.Sprintf("tags: %s", fmt_docking_tags(pob_info.SrpTagList))) + sb.WriteLineStr(fmt.Sprintf("names: %s", fmt_docking_tags(pob_info.SrpNameList))) + sb.WriteLineStr("") + } + + if len(pob_info.AllyFactionHashList) > 0 || len(pob_info.AllyTagList) > 0 || len(pob_info.AllyNameList) > 0 { + sb.WriteLine(InfocardPhrase{Phrase: "Docking allias(IFF rep still affects):", Bold: true}) + sb.WriteLineStr(e.fmt_factions_to_str(factions_by_hash, pob_info.AllyFactionHashList)) + sb.WriteLineStr(fmt.Sprintf("tags: %s", fmt_docking_tags(pob_info.AllyTagList))) + sb.WriteLineStr(fmt.Sprintf("names: %s", fmt_docking_tags(pob_info.AllyNameList))) + sb.WriteLineStr("") + } + + if len(pob_info.HostileFactionHashList) > 0 || len(pob_info.HostileTagList) > 0 || len(pob_info.HostileNameList) > 0 { + sb.WriteLine(InfocardPhrase{Phrase: "Docking enemies:", Bold: true}) + sb.WriteLineStr(e.fmt_factions_to_str(factions_by_hash, pob_info.HostileFactionHashList)) + sb.WriteLineStr(fmt.Sprintf("tags: %s", fmt_docking_tags(pob_info.HostileTagList))) + sb.WriteLineStr(fmt.Sprintf("names: %s", fmt_docking_tags(pob_info.HostileNameList))) + sb.WriteLineStr("") + } + + // TODO add pob infocards here + e.Infocards[InfocardKey(pob.Nickname)] = sb.Lines + e.Configs.Infocards.Infonames[int(flhash.HashNickname(pob.Nickname))] = infocard.Infoname(pob.Name) + + pobs = append(pobs, pob) + } + return pobs +} + +type PobShopItem struct { + *ShopItem + PoBName string + PobNickname string + + System *universe_mapped.System + SystemNick string + SystemName string + FactionNick string + FactionName string + BasePos *cfg.Vector +} + +func (e *Exporter) get_pob_buyable() map[string][]*PobShopItem { + if e.pob_buyable_cache != nil { + return e.pob_buyable_cache + } + + e.pob_buyable_cache = make(map[string][]*PobShopItem) + + // TODO refactor copy repeated code may be + systems_by_hash := make(map[flhash.HashCode]*universe_mapped.System) + factions_by_hash := make(map[flhash.HashCode]*initialworld.Group) + for _, system_info := range e.Configs.Universe.Systems { + nickname := system_info.Nickname.Get() + system_hash := flhash.HashNickname(nickname) + systems_by_hash[system_hash] = system_info + } + for _, group_info := range e.Configs.InitialWorld.Groups { + nickname := group_info.Nickname.Get() + group_hash := flhash.HashFaction(nickname) + factions_by_hash[group_hash] = group_info + } + goods_by_hash := make(map[flhash.HashCode]*equip_mapped.Item) + for _, item := range e.Configs.Equip.Items { + nickname := item.Nickname.Get() + hash := flhash.HashNickname(nickname) + goods_by_hash[hash] = item + e.exportInfocards(InfocardKey(nickname), item.IdsInfo.Get()) + } + ships_by_hash := make(map[flhash.HashCode]*equipment_mapped.Ship) + for _, item := range e.Configs.Goods.Ships { + nickname := item.Nickname.Get() + hash := flhash.HashNickname(nickname) + ships_by_hash[hash] = item + } + + for _, pob_info := range e.Configs.Discovery.PlayerOwnedBases.Bases { + for _, shop_item := range pob_info.ShopItems { + var good *ShopItem = &ShopItem{ShopItem: shop_item} + if item, ok := goods_by_hash[flhash.HashCode(shop_item.Id)]; ok { + good.Nickname = item.Nickname.Get() + good.Name = e.GetInfocardName(item.IdsName.Get(), item.Nickname.Get()) + good.Category = item.Category + } else { + if ship, ok := ships_by_hash[flhash.HashCode(shop_item.Id)]; ok { + ship_hull := e.Configs.Goods.ShipHullsMap[ship.Hull.Get()] + ship_nickname := ship_hull.Ship.Get() + shiparch := e.Configs.Shiparch.ShipsMap[ship_nickname] + good.Nickname = ship_nickname + good.Category = "ship" + good.Name = e.GetInfocardName(shiparch.IdsName.Get(), ship_nickname) + } else { + logus.Log.Warn("unidentified shop item", typelog.Any("shop_item.Id", shop_item.Id)) + } + } + pob_item := &PobShopItem{ + ShopItem: good, + PobNickname: pob_info.Nickname, + PoBName: pob_info.Name, + } + + if pob_info.SystemHash != nil { + if system, ok := systems_by_hash[*pob_info.SystemHash]; ok { + pob_item.SystemNick = system.Nickname.Get() + pob_item.SystemName = e.GetInfocardName(system.StridName.Get(), system.Nickname.Get()) + pob_item.System = system + } + } + if pob_info.AffiliationHash != nil { + if faction, ok := factions_by_hash[*pob_info.AffiliationHash]; ok { + pob_item.FactionNick = faction.Nickname.Get() + pob_item.FactionName = e.GetInfocardName(faction.IdsName.Get(), faction.Nickname.Get()) + } + } + + if pob_info.Pos != nil { + pob_item.BasePos = StrPosToVectorPos(*pob_info.Pos) + } + + e.pob_buyable_cache[good.Nickname] = append(e.pob_buyable_cache[good.Nickname], pob_item) + } + } + return e.pob_buyable_cache +} + +func (e *Exporter) fmt_factions_to_str(factions_by_hash map[flhash.HashCode]*initialworld.Group, faction_hashes []*flhash.HashCode) string { + var sb strings.Builder + + sb.WriteString("factions: [") + + for index, faction_hash := range faction_hashes { + if faction, ok := factions_by_hash[*faction_hash]; ok { + sb.WriteString(e.GetInfocardName(faction.IdsName.Get(), faction.Nickname.Get())) + if index != len(faction_hashes)-1 { + sb.WriteString(", ") + } + } else { + logus.Log.Error("faction hash is invalid", typelog.Any("hash", *faction_hash)) + } + } + sb.WriteString("]") + return sb.String() +} + +func fmt_docking_tags(tags_or_names []string) string { + return fmt.Sprintf("[%s]", strings.Join(tags_or_names, ", ")) +} + +func StrPosToVectorPos(value string) *cfg.Vector { + coords := strings.Split(value, ",") + x, err1 := strconv.ParseFloat(strings.ReplaceAll(coords[0], " ", ""), 64) + y, err2 := strconv.ParseFloat(strings.ReplaceAll(coords[1], " ", ""), 64) + z, err3 := strconv.ParseFloat(strings.ReplaceAll(coords[2], " ", ""), 64) + logus.Log.CheckPanic(err1, "failed parsing x coord", typelog.Any("pos", value)) + logus.Log.CheckPanic(err2, "failed parsing y coord", typelog.Any("pos", value)) + logus.Log.CheckPanic(err3, "failed parsing z coord", typelog.Any("pos", value)) + + return &cfg.Vector{X: x, Y: y, Z: z} +} diff --git a/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/pobs_craftable.go b/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/pobs_craftable.go new file mode 100644 index 0000000..f0fe1f3 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/pobs_craftable.go @@ -0,0 +1,146 @@ +package configs_export + +import ( + "strings" + + "github.com/darklab8/fl-darkstat/configs/cfg" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/inireader" +) + +func (e *Exporter) pob_produced() map[string]bool { + if e.craftable_cached != nil { + return e.craftable_cached + } + + e.craftable_cached = make(map[string]bool) + + if e.Configs.Discovery != nil { + for _, recipe := range e.Configs.Discovery.BaseRecipeItems.Recipes { + for _, produced := range recipe.ProcucedItem { + e.craftable_cached[produced.Get()] = true + } + } + } + + if e.Configs.FLSR != nil { + for _, recipe := range e.Configs.FLSR.FLSRRecipes.Products { + e.craftable_cached[recipe.Product.Get()] = true + } + } + + return e.craftable_cached +} + +const ( + pob_crafts_nickname = "crafts" +) + +func (e *Exporter) EnhanceBasesWithPobCrafts(bases []*Base) []*Base { + pob_produced := e.pob_produced() + + base := &Base{ + Name: e.Configs.CraftableBaseName(), + MarketGoodsPerNick: make(map[CommodityKey]*MarketGood), + Nickname: cfg.BaseUniNick(pob_crafts_nickname), + InfocardKey: InfocardKey(pob_crafts_nickname), + SystemNickname: "neverwhere", + System: "Neverwhere", + Region: "Neverwhere", + FactionName: "Player Crafts", + } + + base.Archetypes = append(base.Archetypes, pob_crafts_nickname) + + for produced, _ := range pob_produced { + market_good := &MarketGood{ + GoodInfo: e.GetGoodInfo(produced), + BaseSells: true, + ShipClass: -1, + IsServerSideOverride: true, + } + e.Hashes[market_good.Nickname] = market_good.NicknameHash + + market_good_key := GetCommodityKey(market_good.Nickname, market_good.ShipClass) + base.MarketGoodsPerNick[market_good_key] = market_good + + var infocard_addition InfocardBuilder + if e.Configs.Discovery != nil { + if recipes, ok := e.Configs.Discovery.BaseRecipeItems.RecipePerProduced[market_good.Nickname]; ok { + infocard_addition.WriteLineStr(`CRAFTING RECIPES:`) + for _, recipe := range recipes { + sector := recipe.Model.RenderModel() + infocard_addition.WriteLineStr(string(sector.OriginalType)) + for _, param := range sector.Params { + infocard_addition.WriteLineStr(string(param.ToString(inireader.WithComments(false)))) + } + infocard_addition.WriteLineStr("") + } + } + } + if e.Configs.FLSR != nil { + if e.Configs.FLSR.FLSRRecipes != nil { + if recipes, ok := e.Configs.FLSR.FLSRRecipes.ProductsByNick[market_good.Nickname]; ok { + infocard_addition.WriteLineStr(`CRAFTING RECIPES:`) + for _, recipe := range recipes { + sector := recipe.Model.RenderModel() + infocard_addition.WriteLineStr(string(sector.OriginalType)) + for _, param := range sector.Params { + infocard_addition.WriteLineStr(string(param.ToString(inireader.WithComments(false)))) + } + infocard_addition.WriteLineStr("") + } + } + } + } + + var info InfocardBuilder + if value, ok := e.Infocards[InfocardKey(market_good.Nickname)]; ok { + info.Lines = value + } + + add_line_about_recipes := func(info Infocard) Infocard { + add_line := func(index int, line InfocardLine) { + info = append(info[:index+1], info[index:]...) + info[index] = line + } + strip_line := func(line string) string { + return strings.ReplaceAll(strings.ReplaceAll(line, " ", ""), "\u00a0", "") + } + if len(infocard_addition.Lines) > 0 { + line_position := 1 + add_line(line_position, InfocardLine{Phrases: []InfocardPhrase{{Phrase: `Item has crafting recipes below`, Bold: true}}}) + if strip_line(info[0].ToStr()) != "" { + add_line(1, NewInfocardSimpleLine("")) + line_position += 1 + } + if strip_line(info[line_position+1].ToStr()) != "" { + add_line(line_position+1, NewInfocardSimpleLine("")) + } + } + return info + } + info.Lines = add_line_about_recipes(info.Lines) + + e.Infocards[InfocardKey(market_good.Nickname)] = append(info.Lines, infocard_addition.Lines...) + + if market_good.ShipNickname != "" { + var info Infocard + if value, ok := e.Infocards[InfocardKey(market_good.ShipNickname)]; ok { + info = value + } + info = add_line_about_recipes(info) + e.Infocards[InfocardKey(market_good.ShipNickname)] = append(info, infocard_addition.Lines...) + } + } + + var sb InfocardBuilder + sb.WriteLineStr(base.Name) + sb.WriteLineStr(`This is only pseudo base to show availability of player crafts`) + sb.WriteLineStr(``) + sb.WriteLineStr(`At the bottom of each item infocard it shows CRAFTING RECIPES`) + + e.Infocards[InfocardKey(base.Nickname)] = sb.Lines + + bases = append(bases, base) + return bases +} diff --git a/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/routes.go b/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/routes.go new file mode 100644 index 0000000..843b973 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/routes.go @@ -0,0 +1,139 @@ +package configs_export + +import ( + "github.com/darklab8/fl-darkstat/configs/cfg" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/universe_mapped" + "github.com/darklab8/fl-darkstat/darkstat/configs_export/trades" +) + +type Route struct { + g *GraphResults + is_disabled bool + from_base_nickname string + to_base_nickname string +} + +type BaseRoute struct { + *Route + FromBase *Base + ToBase *Base +} + +func NewBaseRoute(g *GraphResults, FromBase *Base, ToBase *Base) *BaseRoute { + return &BaseRoute{ + Route: NewRoute(g, FromBase.Nickname.ToStr(), ToBase.Nickname.ToStr()), + FromBase: FromBase, + ToBase: ToBase, + } + +} + +type baseAllRoutes struct { + AllRoutes []*ComboRoute +} + +type ComboRoute struct { + Transport *BaseRoute + Frigate *BaseRoute + Freighter *BaseRoute +} + +func NewRoute(g *GraphResults, from_base_nickname string, to_base_nickname string) *Route { + return &Route{ + g: g, + from_base_nickname: from_base_nickname, + to_base_nickname: to_base_nickname, + } +} + +func (c *Route) GetID() string { + if c.is_disabled { + return "" + } + return c.from_base_nickname + c.to_base_nickname +} + +func (t *Route) GetCruiseSpeed() int { + if t.is_disabled { + return 0 + } + return t.g.Graph.AvgCruiseSpeed +} + +func (t *Route) GetCanVisitFreighterOnlyJH() bool { + if t.is_disabled { + return false + } + return bool(t.g.Graph.CanVisitFreightersOnlyJHs) +} + +type PathWithNavmap struct { + trades.DetailedPath + SectorCoord string + Pos cfg.Vector +} + +func (t *Route) GetPaths() []PathWithNavmap { + var results []PathWithNavmap + paths := t.g.Graph.GetPaths(t.g.Parents, t.g.Time, t.from_base_nickname, t.to_base_nickname) + + for _, path := range paths { + // path.NextName // nickname of object + + augmented_path := PathWithNavmap{ + DetailedPath: path, + } + + if jh, ok := t.g.e.Configs.Systems.JumpholesByNick[path.NextName]; ok { + pos := jh.Pos.Get() + + system_uni := t.g.e.Configs.Universe.SystemMap[universe_mapped.SystemNickname(jh.System.Nickname)] + augmented_path.SectorCoord = VectorToSectorCoord(system_uni, pos) + augmented_path.Pos = pos + } + if base, ok := t.g.e.Configs.Systems.BasesByDockWith[path.NextName]; ok { + pos := base.Pos.Get() + + system_uni := t.g.e.Configs.Universe.SystemMap[universe_mapped.SystemNickname(base.System.Nickname)] + augmented_path.SectorCoord = VectorToSectorCoord(system_uni, pos) + augmented_path.Pos = pos + } + + results = append(results, augmented_path) + } + return results +} + +func (t *Route) GetNameByIdsName(ids_name int) string { + return string(t.g.e.Configs.Infocards.Infonames[ids_name]) +} + +func (t *Route) GetTimeMs() cfg.MillisecondsI { + return trades.GetTimeMs2(t.g.Graph, t.g.Time, t.from_base_nickname, t.to_base_nickname) +} + +func (t *Route) GetTimeS() cfg.Seconds { + return float64(t.GetTimeMs())/trades.PrecisionMultipiler + float64(trades.BaseDockingDelay) +} + +func (e *Exporter) AllRoutes( + bases []*Base, +) []*Base { + for _, from_base := range bases { + for _, to_base := range bases { + // it can fly everywhere so we use it for checking + freighter_route := NewBaseRoute(e.Freighter, from_base, to_base) + + if freighter_route.GetTimeMs() > trades.INF/2 { + continue + } + + from_base.AllRoutes = append(from_base.AllRoutes, &ComboRoute{ + Transport: NewBaseRoute(e.Transport, from_base, to_base), + Frigate: NewBaseRoute(e.Frigate, from_base, to_base), + Freighter: freighter_route, + }) + } + } + return bases +} diff --git a/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/routes_trades.go b/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/routes_trades.go new file mode 100644 index 0000000..691403b --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/routes_trades.go @@ -0,0 +1,162 @@ +package configs_export + +import "github.com/darklab8/fl-darkstat/configs/cfg" + +type TradeRoute struct { + Route *Route + Commodity *Commodity + BuyingGood *MarketGood + SellingGood *MarketGood +} + +func NewTradeRoute(g *GraphResults, buying_good *MarketGood, selling_good *MarketGood, commodity *Commodity) *TradeRoute { + if g == nil { + return &TradeRoute{Route: &Route{is_disabled: true}} + } + + route := &TradeRoute{ + Route: NewRoute(g, buying_good.BaseNickname.ToStr(), selling_good.BaseNickname.ToStr()), + BuyingGood: buying_good, + SellingGood: selling_good, + Commodity: commodity, + } + + return route +} + +func (t *TradeRoute) GetProffitPerV() float64 { + if t.Route.is_disabled { + return 0 + } + + if t.SellingGood.GetPriceBaseBuysFor()-t.BuyingGood.PriceBaseSellsFor == 0 { + return 0 + } + + return float64(t.SellingGood.GetPriceBaseBuysFor()-t.BuyingGood.PriceBaseSellsFor) / float64(t.Commodity.Volume) +} + +func (t *TradeRoute) GetProffitPerTime() float64 { + return t.GetProffitPerV() / t.Route.GetTimeS() +} + +type baseAllTradeRoutes struct { + TradeRoutes []*ComboTradeRoute + BestTransportRoute *TradeRoute + BestFrigateRoute *TradeRoute + BestFreighterRoute *TradeRoute +} + +type ComboTradeRoute struct { + Transport *TradeRoute + Frigate *TradeRoute + Freighter *TradeRoute +} + +func (e *Exporter) TradePaths( + bases []*Base, + commodities []*Commodity, +) ([]*Base, []*Commodity) { + + var commodity_by_nick map[CommodityKey]*Commodity = make(map[CommodityKey]*Commodity) + var commodity_by_good_and_base map[CommodityKey]map[cfg.BaseUniNick]*MarketGood = make(map[CommodityKey]map[cfg.BaseUniNick]*MarketGood) + for _, commodity := range commodities { + commodity_key := GetCommodityKey(commodity.Nickname, commodity.ShipClass) + commodity_by_nick[commodity_key] = commodity + if _, ok := commodity_by_good_and_base[commodity_key]; !ok { + commodity_by_good_and_base[commodity_key] = make(map[cfg.BaseUniNick]*MarketGood) + } + for _, good_at_base := range commodity.Bases { + commodity_by_good_and_base[commodity_key][good_at_base.BaseNickname] = good_at_base + } + } + + for _, base := range bases { + for _, good := range base.MarketGoodsPerNick { + if good.Category != "commodity" { + continue + } + + if !good.BaseSells { + continue + } + + commodity_key := GetCommodityKey(good.Nickname, good.ShipClass) + commodity := commodity_by_nick[commodity_key] + buying_good := commodity_by_good_and_base[commodity_key][base.Nickname] + + if buying_good == nil { + continue + } + + for _, selling_good_at_base := range commodity.Bases { + trade_route := &ComboTradeRoute{ + Transport: NewTradeRoute(e.Transport, buying_good, selling_good_at_base, commodity), + Frigate: NewTradeRoute(e.Frigate, buying_good, selling_good_at_base, commodity), + Freighter: NewTradeRoute(e.Freighter, buying_good, selling_good_at_base, commodity), + } + + if trade_route.Transport.GetProffitPerV() <= 0 { + continue + } + + // If u need to limit to specific min distance + // if trade_route.Transport.GetTime() < 60*10*350 { + // continue + // } + + // fmt.Println("path for", trade_route.Transport.BuyingGood.BaseNickname, trade_route.Transport.SellingGood.BaseNickname) + // fmt.Println("trade_route.Transport.GetPaths().length", len(trade_route.Transport.GetPaths())) + + base.TradeRoutes = append(base.TradeRoutes, trade_route) + commodity.TradeRoutes = append(commodity.TradeRoutes, trade_route) + } + } + } + + for _, commodity := range commodities { + for _, trade_route := range commodity.TradeRoutes { + if commodity.BestTransportRoute == nil { + commodity.BestTransportRoute = trade_route.Transport + } else if trade_route.Transport.GetProffitPerTime() > commodity.BestTransportRoute.GetProffitPerTime() { + commodity.BestTransportRoute = trade_route.Transport + } + + if commodity.BestFreighterRoute == nil { + commodity.BestFreighterRoute = trade_route.Freighter + } else if trade_route.Freighter.GetProffitPerTime() > commodity.BestFreighterRoute.GetProffitPerTime() { + commodity.BestFreighterRoute = trade_route.Freighter + } + + if commodity.BestFrigateRoute == nil { + commodity.BestFrigateRoute = trade_route.Frigate + } else if trade_route.Frigate.GetProffitPerTime() > commodity.BestFrigateRoute.GetProffitPerTime() { + commodity.BestFrigateRoute = trade_route.Frigate + } + } + } + + for _, base := range bases { + for _, trade_route := range base.TradeRoutes { + if base.BestTransportRoute == nil { + base.BestTransportRoute = trade_route.Transport + } else if trade_route.Transport.GetProffitPerTime() > base.BestTransportRoute.GetProffitPerTime() { + base.BestTransportRoute = trade_route.Transport + } + + if base.BestFreighterRoute == nil { + base.BestFreighterRoute = trade_route.Freighter + } else if trade_route.Freighter.GetProffitPerTime() > base.BestFreighterRoute.GetProffitPerTime() { + base.BestFreighterRoute = trade_route.Freighter + } + + if base.BestFrigateRoute == nil { + base.BestFrigateRoute = trade_route.Frigate + } else if trade_route.Frigate.GetProffitPerTime() > base.BestFrigateRoute.GetProffitPerTime() { + base.BestFrigateRoute = trade_route.Frigate + } + } + } + + return bases, commodities +} diff --git a/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/scanners.go b/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/scanners.go new file mode 100644 index 0000000..f806d24 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/scanners.go @@ -0,0 +1,80 @@ +package configs_export + +import ( + "github.com/darklab8/fl-darkstat/configs/cfg" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/initialworld/flhash" +) + +type Scanner struct { + Name string `json:"name"` + Price int `json:"price"` + + Range int `json:"range"` + CargoScanRange int `json:"cargo_scan_range"` + + Lootable bool `json:"lootable"` + Nickname string `json:"nickname"` + NicknameHash flhash.HashCode `json:"nickname_hash" format:"int64"` + NameID int `json:"name_id"` + InfoID int `json:"info_id"` + + Bases map[cfg.BaseUniNick]*MarketGood `json:"-" swaggerignore:"true"` + + *DiscoveryTechCompat `json:"-" swaggerignore:"true"` + Mass float64 `json:"mass"` +} + +func (b Scanner) GetNickname() string { return string(b.Nickname) } + +func (b Scanner) GetBases() map[cfg.BaseUniNick]*MarketGood { return b.Bases } + +func (b Scanner) GetDiscoveryTechCompat() *DiscoveryTechCompat { return b.DiscoveryTechCompat } + +func (e *Exporter) GetScanners(ids []*Tractor) []Scanner { + var scanners []Scanner + + for _, scanner_info := range e.Configs.Equip.Scanners { + item := Scanner{ + Bases: make(map[cfg.BaseUniNick]*MarketGood), + } + item.Mass, _ = scanner_info.Mass.GetValue() + + item.Nickname = scanner_info.Nickname.Get() + item.NicknameHash = flhash.HashNickname(item.Nickname) + e.Hashes[item.Nickname] = item.NicknameHash + + item.Lootable = scanner_info.Lootable.Get() + item.NameID = scanner_info.IdsName.Get() + item.InfoID = scanner_info.IdsInfo.Get() + item.Range = scanner_info.Range.Get() + item.CargoScanRange = scanner_info.CargoScanRange.Get() + + if good_info, ok := e.Configs.Goods.GoodsMap[item.Nickname]; ok { + if price, ok := good_info.Price.GetValue(); ok { + item.Price = price + item.Bases = e.GetAtBasesSold(GetCommodityAtBasesInput{ + Nickname: good_info.Nickname.Get(), + Price: price, + }) + } + } + + item.Name = e.GetInfocardName(item.NameID, item.Nickname) + + e.exportInfocards(InfocardKey(item.Nickname), item.InfoID) + item.DiscoveryTechCompat = CalculateTechCompat(e.Configs.Discovery, ids, item.Nickname) + scanners = append(scanners, item) + } + return scanners +} + +func (e *Exporter) FilterToUserfulScanners(items []Scanner) []Scanner { + var useful_items []Scanner = make([]Scanner, 0, len(items)) + for _, item := range items { + if !e.Buyable(item.Bases) { + continue + } + useful_items = append(useful_items, item) + } + return useful_items +} diff --git a/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/shields.go b/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/shields.go new file mode 100644 index 0000000..bbffb63 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/shields.go @@ -0,0 +1,144 @@ +package configs_export + +import ( + "math" + "regexp" + + "github.com/darklab8/fl-darkstat/configs/cfg" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/initialworld/flhash" + "github.com/darklab8/fl-darkstat/configs/configs_settings/logus" + "github.com/darklab8/go-typelog/typelog" +) + +func (g Shield) GetTechCompat() *DiscoveryTechCompat { return g.DiscoveryTechCompat } + +type Shield struct { + Name string `json:"name"` + + Class string `json:"class"` + Type string `json:"type"` + Technology string `json:"technology"` + Price int `json:"price"` + + Capacity int `json:"capacity"` + RegenerationRate int `json:"regeneration_rate"` + ConstantPowerDraw int `json:"constant_power_draw"` + Value float64 `json:"value"` + RebuildPowerDraw int `json:"rebuild_power_draw"` + OffRebuildTime int `json:"off_rebuild_time"` + + Toughness float64 `json:"toughness"` + HitPts int `json:"hit_pts"` + Lootable bool `json:"lootable"` + + Nickname string `json:"nickname"` + HpType string `json:"hp_type"` + NicknameHash flhash.HashCode `json:"nickname_hash" format:"int64"` + HpTypeHash flhash.HashCode `json:"-" swaggerignore:"true"` + IdsName int `json:"ids_name"` + IdsInfo int `json:"ids_info"` + + Bases map[cfg.BaseUniNick]*MarketGood `json:"-" swaggerignore:"true"` + + *DiscoveryTechCompat `json:"-" swaggerignore:"true"` + Mass float64 `json:"mass"` +} + +func (b Shield) GetNickname() string { return string(b.Nickname) } +func (b Shield) GetBases() map[cfg.BaseUniNick]*MarketGood { return b.Bases } + +func (b Shield) GetDiscoveryTechCompat() *DiscoveryTechCompat { return b.DiscoveryTechCompat } + +func (e *Exporter) GetShields(ids []*Tractor) []Shield { + var shields []Shield + + for _, shield_gen := range e.Configs.Equip.ShieldGens { + shield := Shield{ + Nickname: shield_gen.Nickname.Get(), + + IdsInfo: shield_gen.IdsInfo.Get(), + IdsName: shield_gen.IdsName.Get(), + Bases: make(map[cfg.BaseUniNick]*MarketGood), + } + shield.Mass, _ = shield_gen.Mass.GetValue() + + shield.NicknameHash = flhash.HashNickname(shield.Nickname) + e.Hashes[shield.Nickname] = shield.NicknameHash + + shield.Technology, _ = shield_gen.ShieldType.GetValue() + shield.Capacity, _ = shield_gen.MaxCapacity.GetValue() + + shield.RegenerationRate = shield_gen.RegenerationRate.Get() + shield.ConstantPowerDraw = shield_gen.ConstPowerDraw.Get() + shield.RebuildPowerDraw = shield_gen.RebuildPowerDraw.Get() + shield.OffRebuildTime, _ = shield_gen.OfflineRebuildTime.GetValue() + + shield.Lootable, _ = shield_gen.Lootable.GetValue() + shield.Toughness, _ = shield_gen.Toughness.GetValue() + shield.HitPts = shield_gen.HitPts.Get() + + if good_info, ok := e.Configs.Goods.GoodsMap[shield.Nickname]; ok { + if price, ok := good_info.Price.GetValue(); ok { + shield.Price = price + shield.Bases = e.GetAtBasesSold(GetCommodityAtBasesInput{ + Nickname: good_info.Nickname.Get(), + Price: price, + }) + + var shield_value float64 + + if shield.Capacity != 0 { + shield_value = math.Abs(float64(shield.Capacity)) + } else if shield.RegenerationRate != 0 { + shield_value = math.Abs(float64(shield.RegenerationRate)) + } else if shield.ConstantPowerDraw != 0 { + shield_value = math.Abs(float64(shield.ConstantPowerDraw)) + } + shield.Value = 1000 * shield_value / float64(shield.Price) + } + } + + shield.Name = e.GetInfocardName(shield.IdsName, shield.Nickname) + + if hp_type, ok := shield_gen.HpType.GetValue(); ok { + shield.HpType = hp_type + shield.HpTypeHash = flhash.HashNickname(shield.HpType) + e.Hashes[shield.HpType] = shield.HpTypeHash + + if parsed_type_class := TypeClassRegex.FindStringSubmatch(hp_type); len(parsed_type_class) > 0 { + shield.Type = parsed_type_class[1] + shield.Class = parsed_type_class[2] + } + } + + e.exportInfocards(InfocardKey(shield.Nickname), shield.IdsInfo) + shield.DiscoveryTechCompat = CalculateTechCompat(e.Configs.Discovery, ids, shield.Nickname) + + shields = append(shields, shield) + } + + return shields +} + +var TypeClassRegex *regexp.Regexp + +func init() { + TypeClassRegex = InitRegexExpression(`[a-zA-Z]+_([a-zA-Z]+)_[a-zA-Z_]+([0-9])`) +} + +func InitRegexExpression(expression string) *regexp.Regexp { + regex, err := regexp.Compile(string(expression)) + logus.Log.CheckPanic(err, "failed to init regex={%s} in ", typelog.String("expression", expression)) + return regex +} + +func (e *Exporter) FilterToUsefulShields(shields []Shield) []Shield { + var items []Shield = make([]Shield, 0, len(shields)) + for _, item := range shields { + if !e.Buyable(item.Bases) { + continue + } + items = append(items, item) + } + return items +} diff --git a/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/ships.go b/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/ships.go new file mode 100644 index 0000000..9fdf29f --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/ships.go @@ -0,0 +1,413 @@ +package configs_export + +import ( + "math" + "sort" + "strings" + + "github.com/darklab8/fl-darkstat/configs/cfg" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/equipment_mapped/equip_mapped" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/initialworld/flhash" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/ship_mapped" + "github.com/darklab8/fl-darkstat/configs/configs_settings/logus" + "github.com/darklab8/go-typelog/typelog" +) + +func (g Ship) GetTechCompat() *DiscoveryTechCompat { return g.DiscoveryTechCompat } + +type ShipPackage struct { + Nickname string + equipped_thrusters []*equip_mapped.Thruster +} + +type Ship struct { + Nickname string `json:"nickname"` + NicknameHash flhash.HashCode `json:"nickname_hash"` + + Name string `json:"name"` + Class int `json:"class"` + Type string `json:"type"` + Price int `json:"price"` + Armor int `json:"armor"` + HoldSize int `json:"hold_size"` + Nanobots int `json:"nanobots"` + Batteries int `json:"batteries"` + Mass float64 `json:"mass"` + + PowerCapacity int `json:"power_capacity"` + PowerRechargeRate int `json:"power_recharge_rate"` + CruiseSpeed int `json:"cruise_speed"` + LinearDrag float64 `json:"linear_drag"` + EngineMaxForce int `json:"engine_max_force"` + ImpulseSpeed float64 `json:"impulse_speed"` + ThrusterSpeed []int `json:"thruster_speed"` + ReverseFraction float64 `json:"reverse_fraction"` + ThrustCapacity int `json:"thrust_capacity"` + ThrustRecharge int `json:"thrust_recharge"` + + MaxAngularSpeedDegS float64 `json:"max_ansgular_speed"` + AngularDistanceFrom0ToHalfSec float64 `json:"angular_distance_from_0_to_halfsec"` + TimeTo90MaxAngularSpeed float64 `json:"time_to_90_max_angular_speed"` + + NudgeForce float64 `json:"nudge_force"` + StrafeForce float64 `json:"strafe_force"` + NameID int `json:"name_id"` + InfoID int `json:"info_id"` + + Bases map[cfg.BaseUniNick]*MarketGood `json:"_" swaggerignore:"true"` + Slots []EquipmentSlot `json:"equipment_slots"` + BiggestHardpoint []string `json:"biggest_hardpoint"` + ShipPackages []ShipPackage `json:"ship_packages"` + + *DiscoveryTechCompat `json:"_" swaggerignore:"true"` + + DiscoShip *DiscoShip `json:"discovery_ship"` +} + +func (b Ship) GetNickname() string { return string(b.Nickname) } + +func (b Ship) GetBases() map[cfg.BaseUniNick]*MarketGood { return b.Bases } + +func (b Ship) GetDiscoveryTechCompat() *DiscoveryTechCompat { return b.DiscoveryTechCompat } + +/* +For each ship + + ship_packages = find buyable/craftable ship packages +*/ +func remove(s []int, i int) []int { + s[i] = s[len(s)-1] + return s[:len(s)-1] +} + +func is_thruster_slot(slot EquipmentSlot) bool { + for _, smth := range slot.AllowedEquip { + if smth == "hp_thruster" { + return true + } + } + return false +} + +func (s *Ship) getThrusterSpeed( + e *Exporter, + equipped_thrusters []*equip_mapped.Thruster, + linear_drag float64, + ship_info *ship_mapped.Ship, + ThrustCapacity float64, + ThrustRecharge float64, + Slots []EquipmentSlot, + ThrusterMap map[string]*Thruster, +) float64 { + // find amount of thrusters + thruster_amount := 0 + for _, slot := range Slots { + if is_thruster_slot(slot) { + thruster_amount++ + } + } + + total_thruster_force := 0 + + // find_max_forced_compatible_thruster + max_thruster_force := 0 + // var found_thruster1 *equip_mapped.Thruster # debug data + // var found_thruster2 *Thruster # debug data + + for _, thruster := range e.Configs.Equip.Thrusters { + thrust_usage := thruster.PowerUsage.Get() + + seconds_thrust_usage := int(ThrustCapacity / (float64(thrust_usage*thruster_amount) - ThrustRecharge)) + // 2000 / (2*120000 - 200) + if seconds_thrust_usage < 0 { + seconds_thrust_usage = 9999 + } + + // exclude not usable. if they are usable less than 3 seconds + if seconds_thrust_usage >= 0 && seconds_thrust_usage < 3 { + continue + } + + // add check if item is buyable or craftable + thruster_info, found_thruster := ThrusterMap[thruster.Nickname.Get()] + if !found_thruster { + continue + } + if !e.Buyable(thruster_info.Bases) { + continue + } + + thruster_force := thruster.MaxForce.Get() + + // no point to select weak + if thruster_force < max_thruster_force { + continue + } + + max_thruster_force = thruster_force + // found_thruster1 = thruster + // found_thruster2 = thruster_info + } + + // _ = found_thruster2 + // _ = found_thruster1 + + // for each thruster at a ship + for i := 0; i < thruster_amount; i++ { + + // if already installed zero price thrustre is installed and has zero price and it is disco + // add its force + if i < len(equipped_thrusters) { + thruster := equipped_thrusters[i] + thruster_price := 0 + if good_info, ok := e.Configs.Goods.GoodsMap[thruster.Nickname.Get()]; ok { + if price, ok := good_info.Price.GetValue(); ok { + thruster_price = price + } + } + if e.Configs.Discovery != nil && thruster_price == 0 { + total_thruster_force += thruster.MaxForce.Get() + continue + } + } + + // else: + // add max forced compatible thruster + total_thruster_force += max_thruster_force + } + + return s.ImpulseSpeed + float64(total_thruster_force)/linear_drag +} + +type DiscoShip struct { + ArmorMult float64 `json:"armor_mult"` +} + +func (e *Exporter) GetShips(ids []*Tractor, TractorsByID map[cfg.TractorID]*Tractor, Thrusters []Thruster) []Ship { + var ships []Ship + + var ThrusterMap map[string]*Thruster = make(map[string]*Thruster) + for _, thruster := range Thrusters { + ThrusterMap[thruster.Nickname] = &thruster + } + + for _, ship_info := range e.Configs.Shiparch.Ships { + ship := Ship{ + Nickname: ship_info.Nickname.Get(), + Bases: make(map[cfg.BaseUniNick]*MarketGood), + } + ship.NicknameHash = flhash.HashNickname(ship.Nickname) + e.Hashes[ship.Nickname] = ship.NicknameHash + + // defer func() { + // if r := recover(); r != nil { + // fmt.Println("Recovered in f", r) + // fmt.Println("ship.Nickname", ship.Nickname) + // panic(r) + // } + // }() + + ship.Class, _ = ship_info.ShipClass.GetValue() + if _, ok := ship_info.Type.GetValue(); !ok { + logus.Log.Warn("ship problem with type", typelog.Any("nickname", ship.Nickname)) + } + ship.Type, _ = ship_info.Type.GetValue() + ship.Type = strings.ToLower(ship.Type) + + if ship_name_id, ship_has_name := ship_info.IdsName.GetValue(); ship_has_name { + ship.NameID = ship_name_id + } else { + logus.Log.Warn("WARNING, ship has no ItdsName", typelog.String("ship.Nickname", ship.Nickname)) + } + + ship.InfoID, _ = ship_info.IdsInfo.GetValue() + + if bots, ok := ship_info.Nanobots.GetValue(); ok { + ship.Nanobots = bots + } else { + continue + } + ship.Batteries = ship_info.Batteries.Get() + ship.Mass = ship_info.Mass.Get() + ship.NudgeForce = ship_info.NudgeForce.Get() + ship.StrafeForce, _ = ship_info.StrafeForce.GetValue() + + ship.Name = e.GetInfocardName(ship.NameID, ship.Nickname) + + if ship_hull_good, ok := e.Configs.Goods.ShipHullsMapByShip[ship.Nickname]; ok { + ship.Price = ship_hull_good.Price.Get() + + ship_hull_nickname := ship_hull_good.Nickname.Get() + if ship_package_goods, ok := e.Configs.Goods.ShipsMapByHull[ship_hull_nickname]; ok { + + for _, ship_package_good := range ship_package_goods { + var equipped_thrusters []*equip_mapped.Thruster + for _, addon := range ship_package_good.Addons { + + // can be Power or Engine or Smth else + // addon = dsy_hessian_engine, HpEngine01, 1 + // addon = dsy_loki_core, internal, 1 + // addon = ge_s_scanner_01, internal, 1 + addon_nickname := addon.ItemNickname.Get() + + if good_info, ok := e.Configs.Goods.GoodsMap[addon_nickname]; ok { + if addon_price, ok := good_info.Price.GetValue(); ok { + ship.Price += addon_price + } + } + if thruster, ok := e.Configs.Equip.ThrusterMap[addon_nickname]; ok { + equipped_thrusters = append(equipped_thrusters, thruster) + } + if power, ok := e.Configs.Equip.PowersMap[addon_nickname]; ok { + ship.PowerCapacity = power.Capacity.Get() + ship.PowerRechargeRate = power.ChargeRate.Get() + + ship.ThrustCapacity = power.ThrustCapacity.Get() + ship.ThrustRecharge = power.ThrustRecharge.Get() + } + if engine, ok := e.Configs.Equip.EnginesMap[addon_nickname]; ok { + ship.CruiseSpeed = e.GetEngineSpeed(engine) + engine_linear_drag, _ := engine.LinearDrag.GetValue() + ship_linear_drag, _ := ship_info.LinearDrag.GetValue() + ship.EngineMaxForce, _ = engine.MaxForce.GetValue() + ship.LinearDrag = (float64(engine_linear_drag) + float64(ship_linear_drag)) + ship.ImpulseSpeed = float64(ship.EngineMaxForce) / ship.LinearDrag + + ship.ReverseFraction = engine.ReverseFraction.Get() + + ship.MaxAngularSpeedDegS = ship_info.SteeringTorque.X.Get() / ship_info.AngularDrag.X.Get() + ship.TimeTo90MaxAngularSpeed = ship_info.RotationIntertia.X.Get() / (ship_info.AngularDrag.X.Get() * LogOgE) + + ship.MaxAngularSpeedDegS *= Pi180 + + if ship.TimeTo90MaxAngularSpeed > 0.5 { + ship.AngularDistanceFrom0ToHalfSec = ship.MaxAngularSpeedDegS * (0.5 / ship.TimeTo90MaxAngularSpeed) / 2 + } else { + ship.AngularDistanceFrom0ToHalfSec = ship.MaxAngularSpeedDegS*(0.5-ship.TimeTo90MaxAngularSpeed) + ship.MaxAngularSpeedDegS*ship.TimeTo90MaxAngularSpeed/2 + } + } + } + + ships_at_bases := e.GetAtBasesSold(GetCommodityAtBasesInput{ + Nickname: ship_package_good.Nickname.Get(), + Price: ship.Price, + }) + for key, value := range ships_at_bases { + ship.Bases[key] = value + } + + ship.ShipPackages = append(ship.ShipPackages, + ShipPackage{ + Nickname: ship_package_good.Nickname.Get(), + equipped_thrusters: equipped_thrusters, + }, + ) + + } + + } + + } + + ship.HoldSize = ship_info.HoldSize.Get() + ship.Armor = ship_info.HitPts.Get() + + var hardpoints map[string][]string = make(map[string][]string) + for _, hp_type := range ship_info.HpTypes { + for _, equipment := range hp_type.AllowedEquipments { + equipment_slot := equipment.Get() + hardpoints[equipment_slot] = append(hardpoints[equipment_slot], hp_type.Nickname.Get()) + } + } + + for slot_name, allowed_equip := range hardpoints { + ship.Slots = append(ship.Slots, EquipmentSlot{ + SlotName: slot_name, + AllowedEquip: allowed_equip, + }) + } + + sort.Slice(ship.Slots, func(i, j int) bool { + return ship.Slots[i].SlotName < ship.Slots[j].SlotName + }) + for _, slot := range ship.Slots { + sort.Slice(slot.AllowedEquip, func(i, j int) bool { + return slot.AllowedEquip[i] < slot.AllowedEquip[j] + }) + } + + for _, slot := range ship.Slots { + if len(slot.AllowedEquip) > len(ship.BiggestHardpoint) { + ship.BiggestHardpoint = slot.AllowedEquip + } + } + + var infocards []int + if id, ok := ship_info.IdsInfo1.GetValue(); ok { + infocards = append(infocards, id) + } + // if id, ok := ship_info.IdsInfo2.GetValue(); ok { + // infocards = append(infocards, id) + // } + // Nobody uses it? + // if id, ok := ship_info.IdsInfo3.GetValue(); ok { + // infocards = append(infocards, id) + // } + if id, ok := ship_info.IdsInfo.GetValue(); ok { + infocards = append(infocards, id) + } + e.exportInfocards(InfocardKey(ship.Nickname), infocards...) + ship.DiscoveryTechCompat = CalculateTechCompat(e.Configs.Discovery, ids, ship.Nickname) + + if e.Configs.Discovery != nil { + armor_mult, _ := ship_info.ArmorMult.GetValue() + ship.DiscoShip = &DiscoShip{ArmorMult: armor_mult} + } + + var thruster_speeds map[int]bool = make(map[int]bool) + for _, ship_package := range ship.ShipPackages { + + thrust_speed := ship.getThrusterSpeed(e, + ship_package.equipped_thrusters, + ship.LinearDrag, + ship_info, + float64(ship.ThrustCapacity), + float64(ship.ThrustRecharge), + ship.Slots, + ThrusterMap, + ) + thruster_speeds[int(thrust_speed)] = true + } + for thrust_speed, _ := range thruster_speeds { + ship.ThrusterSpeed = append(ship.ThrusterSpeed, thrust_speed) + } + + ships = append(ships, ship) + } + + return ships +} + +type EquipmentSlot struct { + SlotName string + AllowedEquip []string +} + +var Pi180 = 180 / math.Pi // number turning radians to degrees +var LogOgE = math.Log10(math.E) + +func (e *Exporter) FilterToUsefulShips(ships []Ship) []Ship { + var items []Ship = make([]Ship, 0, len(ships)) + for _, item := range ships { + if !e.Buyable(item.Bases) { + continue + } + items = append(items, item) + } + return items +} + +type CompatibleIDsForTractor struct { + TechCompat float64 + Tractor *Tractor +} diff --git a/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/tech_compat.go b/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/tech_compat.go new file mode 100644 index 0000000..bfcfdcd --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/tech_compat.go @@ -0,0 +1,68 @@ +package configs_export + +import ( + "sort" + + "github.com/darklab8/fl-darkstat/configs/cfg" + "github.com/darklab8/fl-darkstat/configs/configs_mapped" +) + +type TechCompatOrderer struct { + cached_techcell_nil []CompatibleIDsForTractor + configs *configs_mapped.MappedConfigs + exporter *Exporter +} + +func NewOrderedTechCompat(e *Exporter) *TechCompatOrderer { + orderer := &TechCompatOrderer{ + configs: e.Configs, + exporter: e, + } + + orderer.cached_techcell_nil = append(orderer.cached_techcell_nil, CompatibleIDsForTractor{ + TechCompat: e.Configs.Discovery.Techcompat.General.UnlistedTech.Get(), + Tractor: &Tractor{Name: "Most Factions"}, + }) + + for _, faction := range e.Configs.Discovery.Techcompat.Factions { + if unlisted_faction_modifier, ok := faction.DefaultUnlisted.GetValue(); ok { + orderer.cached_techcell_nil = append(orderer.cached_techcell_nil, CompatibleIDsForTractor{ + TechCompat: unlisted_faction_modifier, + Tractor: orderer.exporter.TractorsByID[cfg.TractorID(faction.ID.Get())], + }) + } + } + + return orderer +} + +func (orderer *TechCompatOrderer) GetOrederedTechCompat(DiscoveryTechCompat *DiscoveryTechCompat) []CompatibleIDsForTractor { + var DiscoIDsCompatsOrdered []CompatibleIDsForTractor + + if DiscoveryTechCompat == nil { + return DiscoIDsCompatsOrdered + } + + if DiscoveryTechCompat.TechCell == "" { + return orderer.cached_techcell_nil + } + + for tractor_id, tech_tecompability := range DiscoveryTechCompat.TechcompatByID { + if tech_tecompability < 11.0/100.0 { + continue + } + + if tractor, ok := orderer.exporter.TractorsByID[tractor_id]; ok { + DiscoIDsCompatsOrdered = append(DiscoIDsCompatsOrdered, CompatibleIDsForTractor{ + TechCompat: tech_tecompability, + Tractor: tractor, + }) + } + } + + sort.Slice(DiscoIDsCompatsOrdered, func(i, j int) bool { + return DiscoIDsCompatsOrdered[i].Tractor.Name < DiscoIDsCompatsOrdered[j].Tractor.Name + }) + + return DiscoIDsCompatsOrdered +} diff --git a/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/thrusters.go b/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/thrusters.go new file mode 100644 index 0000000..73b149a --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/thrusters.go @@ -0,0 +1,112 @@ +package configs_export + +import ( + "github.com/darklab8/fl-darkstat/configs/cfg" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/initialworld/flhash" +) + +type Thruster struct { + Name string `json:"name"` + Price int `json:"price"` + MaxForce int `json:"max_force"` + PowerUsage int `json:"power_usage"` + Efficiency float64 `json:"efficiency"` + Value float64 `json:"value"` + HitPts int `json:"hit_pts"` + Lootable bool `json:"lootable"` + Nickname string `json:"nickname"` + NicknameHash flhash.HashCode `json:"nickname_hash" format:"int64"` + NameID int `json:"name_id"` + InfoID int `json:"info_id"` + + Bases map[cfg.BaseUniNick]*MarketGood `json:"-" swaggerignore:"true"` + + *DiscoveryTechCompat `json:"-" swaggerignore:"true"` + Mass float64 `json:"mass"` +} + +func (b Thruster) GetNickname() string { return string(b.Nickname) } + +func (b Thruster) GetBases() map[cfg.BaseUniNick]*MarketGood { return b.Bases } + +func (b Thruster) GetDiscoveryTechCompat() *DiscoveryTechCompat { return b.DiscoveryTechCompat } + +func (e *Exporter) GetThrusters(ids []*Tractor) []Thruster { + var thrusters []Thruster + + for _, thruster_info := range e.Configs.Equip.Thrusters { + thruster := Thruster{ + Bases: make(map[cfg.BaseUniNick]*MarketGood), + } + thruster.Mass, _ = thruster_info.Mass.GetValue() + + thruster.Nickname = thruster_info.Nickname.Get() + thruster.NicknameHash = flhash.HashNickname(thruster.Nickname) + e.Hashes[thruster.Nickname] = thruster.NicknameHash + + thruster.MaxForce = thruster_info.MaxForce.Get() + thruster.PowerUsage = thruster_info.PowerUsage.Get() + thruster.HitPts = thruster_info.HitPts.Get() + thruster.Lootable = thruster_info.Lootable.Get() + thruster.NameID = thruster_info.IdsName.Get() + thruster.InfoID = thruster_info.IdsInfo.Get() + + if good_info, ok := e.Configs.Goods.GoodsMap[thruster.Nickname]; ok { + if price, ok := good_info.Price.GetValue(); ok { + thruster.Price = price + thruster.Bases = e.GetAtBasesSold(GetCommodityAtBasesInput{ + Nickname: good_info.Nickname.Get(), + Price: price, + }) + } + } + + thruster.Name = e.GetInfocardName(thruster.NameID, thruster.Nickname) + + /* + Copy paste of Adoxa's changelog + * Efficiency: max_force / power; + power if max_force is 0; + * Value: max_force / price; + power * 1000 / price if max_force is 0; + * Rating: max_force / (power - 100) * Value / 1000 (where 100 is the standard + thrust recharge rate). + */ + + power_usage_calc := thruster.PowerUsage + if power_usage_calc == 0 { + power_usage_calc = 1 + } + if thruster.MaxForce > 0 { + thruster.Efficiency = float64(thruster.MaxForce) / float64(power_usage_calc) + } else { + thruster.Efficiency = float64(thruster.PowerUsage) + } + + price_calc := thruster.Price + if price_calc == 0 { + price_calc = 1 + } + if thruster.MaxForce > 0 { + thruster.Value = float64(thruster.MaxForce) / float64(price_calc) + } else { + thruster.Value = float64(thruster.Price) * 1000 + } + + e.exportInfocards(InfocardKey(thruster.Nickname), thruster.InfoID) + thruster.DiscoveryTechCompat = CalculateTechCompat(e.Configs.Discovery, ids, thruster.Nickname) + thrusters = append(thrusters, thruster) + } + return thrusters +} + +func (e *Exporter) FilterToUsefulThrusters(thrusters []Thruster) []Thruster { + var items []Thruster = make([]Thruster, 0, len(thrusters)) + for _, item := range thrusters { + if !e.Buyable(item.Bases) { + continue + } + items = append(items, item) + } + return items +} diff --git a/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/tractors.go b/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/tractors.go new file mode 100644 index 0000000..bc2b7b3 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/tractors.go @@ -0,0 +1,165 @@ +package configs_export + +import ( + "fmt" + "sort" + "strings" + + "github.com/darklab8/fl-darkstat/configs/cfg" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/initialworld/flhash" + "github.com/darklab8/fl-darkstat/configs/configs_settings/logus" + "github.com/darklab8/fl-darkstat/configs/discovery/playercntl_rephacks" + "github.com/darklab8/go-typelog/typelog" +) + +type Rephack struct { + FactionName string `json:"faction_name"` + FactionNick cfg.FactionNick `json:"faction_nickname"` + Reputation float64 `json:"reputation"` + RepType playercntl_rephacks.RepType `json:"rep_type"` +} + +type DiscoveryIDRephacks struct { + Rephacks map[cfg.FactionNick]Rephack `json:"rephacks"` +} + +func (r DiscoveryIDRephacks) GetRephacksList() []Rephack { + + var result []Rephack + for _, rephack := range r.Rephacks { + + result = append(result, rephack) + } + sort.Slice(result, func(i, j int) bool { + return result[i].Reputation > result[j].Reputation + }) + return result + +} + +type Tractor struct { + Name string `json:"name"` + Price int `json:"price"` + MaxLength int `json:"max_length"` + ReachSpeed int `json:"reach_speed"` + + Lootable bool `json:"lootable"` + Nickname cfg.TractorID `json:"nickname"` + NicknameHash flhash.HashCode `json:"nickname_hash" format:"int64"` + ShortNickname string `json:"short_nickname"` + NameID int `json:"name_id"` + InfoID int `json:"info_id"` + + Bases map[cfg.BaseUniNick]*MarketGood `json:"-" swaggerignore:"true"` + DiscoveryIDRephacks + Mass float64 `json:"mass"` +} + +func (e *Exporter) GetFactionName(nickname cfg.FactionNick) string { + if group, ok := e.Configs.InitialWorld.GroupsMap[string(nickname)]; ok { + return e.GetInfocardName(group.IdsName.Get(), string(nickname)) + } + return "" +} + +func (e *Exporter) GetTractors() []*Tractor { + var tractors []*Tractor + + for tractor_id, tractor_info := range e.Configs.Equip.Tractors { + tractor := &Tractor{ + Nickname: cfg.TractorID(tractor_info.Nickname.Get()), + ShortNickname: fmt.Sprintf("i%d", tractor_id), + DiscoveryIDRephacks: DiscoveryIDRephacks{ + Rephacks: make(map[cfg.FactionNick]Rephack), + }, + Bases: make(map[cfg.BaseUniNick]*MarketGood), + } + + if _, ok := tractor_info.IdsName.GetValue(); !ok { + logus.Log.Warn("tractor is not having defined ids_name", typelog.Any("nickname", tractor.Nickname)) + } + tractor.Mass, _ = tractor_info.Mass.GetValue() + + tractor.NicknameHash = flhash.HashNickname(string(tractor.Nickname)) + e.Hashes[string(tractor.Nickname)] = tractor.NicknameHash + + tractor.MaxLength = tractor_info.MaxLength.Get() + tractor.ReachSpeed = tractor_info.ReachSpeed.Get() + tractor.Lootable = tractor_info.Lootable.Get() + tractor.NameID, _ = tractor_info.IdsName.GetValue() + tractor.InfoID, _ = tractor_info.IdsInfo.GetValue() + + if good_info, ok := e.Configs.Goods.GoodsMap[string(tractor.Nickname)]; ok { + if price, ok := good_info.Price.GetValue(); ok { + tractor.Price = price + tractor.Bases = e.GetAtBasesSold(GetCommodityAtBasesInput{ + Nickname: good_info.Nickname.Get(), + Price: price, + }) + } + } + + tractor.Name = e.GetInfocardName(tractor.NameID, string(tractor.Nickname)) + + e.exportInfocards(InfocardKey(tractor.Nickname), tractor.InfoID) + + if e.Configs.Discovery != nil { + + for faction_nick, faction := range e.Configs.Discovery.PlayercntlRephacks.DefaultReps { + tractor.Rephacks[faction_nick] = Rephack{ + Reputation: faction.Rep.Get(), + RepType: faction.GetRepType(), + FactionNick: faction_nick, + FactionName: e.GetFactionName(faction_nick), + } + } + + if faction, ok := e.Configs.Discovery.PlayercntlRephacks.RephacksByID[tractor.Nickname]; ok { + + if inherited_id, ok := faction.Inherits.GetValue(); ok { + if faction, ok := e.Configs.Discovery.PlayercntlRephacks.RephacksByID[cfg.TractorID(inherited_id)]; ok { + for faction_nick, rep := range faction.Reps { + tractor.Rephacks[faction_nick] = Rephack{ + Reputation: rep.Rep.Get(), + RepType: rep.GetRepType(), + FactionNick: faction_nick, + FactionName: e.GetFactionName(faction_nick), + } + } + } + } + + for faction_nick, rep := range faction.Reps { + tractor.Rephacks[faction_nick] = Rephack{ + Reputation: rep.Rep.Get(), + RepType: rep.GetRepType(), + FactionNick: faction_nick, + FactionName: e.GetFactionName(faction_nick), + } + } + } + } + tractors = append(tractors, tractor) + } + return tractors +} + +func (b Tractor) GetNickname() string { return string(b.Nickname) } + +func (b Tractor) GetBases() map[cfg.BaseUniNick]*MarketGood { return b.Bases } + +func (e *Exporter) FilterToUsefulTractors(tractors []*Tractor) []*Tractor { + var buyable_tractors []*Tractor = make([]*Tractor, 0, len(tractors)) + for _, item := range tractors { + + if !e.Buyable(item.Bases) && (strings.Contains(strings.ToLower(item.Name), "discontinued") || + strings.Contains(strings.ToLower(item.Name), "not in use") || + strings.Contains(strings.ToLower(item.Name), strings.ToLower("Special Operative ID")) || + strings.Contains(strings.ToLower(item.Name), strings.ToLower("SRP ID")) || + strings.Contains(strings.ToLower(item.Name), strings.ToLower("Unused"))) { + continue + } + buyable_tractors = append(buyable_tractors, item) + } + return buyable_tractors +} diff --git a/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/trades/dijkstra_apsp.go b/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/trades/dijkstra_apsp.go new file mode 100644 index 0000000..5d20c75 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/trades/dijkstra_apsp.go @@ -0,0 +1,242 @@ +package trades + +import ( + "container/heap" + "math" +) + +// Written based on https://www.geeksforgeeks.org/implementation-of-johnsons-algorithm-for-all-pairs-shortest-paths/ +// And then rewritten in a way that there is nothing left from Johnson almost. +// It is now DijkstraAPSP https://en.wikipedia.org/wiki/Parallel_all-pairs_shortest_path_algorithm + +type Neighbour struct { + destination int + weight int +} + +func NewNeighbour(destination int, weight int) *Neighbour { + return &Neighbour{destination: destination, + weight: weight, + } +} + +type DijkstraAPSP struct { + vertices int + adjacencyList [][]*Neighbour + allowed_base_ids map[int]bool +} + +const INF = math.MaxInt +const INFthreshold = math.MaxInt / 1000 + +// On using the below constructor, +// edges must be added manually +// to the graph using addEdge() +func NewDijkstraApsp(vertices int) *DijkstraAPSP { + g := &DijkstraAPSP{ + vertices: vertices, + allowed_base_ids: make(map[int]bool), + } + + g.adjacencyList = make([][]*Neighbour, vertices) + for i := 0; i < vertices; i++ { + g.adjacencyList[i] = make([]*Neighbour, 0) + } + + return g +} + +// // On using the below constructor, +// // edges will be added automatically +// // to the graph using the adjacency matrix +func NewDijkstraApspFromMatrix(vertices int, adjacencyMatrix [][]int) *DijkstraAPSP { + g := NewDijkstraApsp(vertices) + + for i := 0; i < vertices; i++ { + for j := 0; j < vertices; j++ { + if adjacencyMatrix[i][j] != 0 { + g.addEdge(i, j, adjacencyMatrix[i][j]) + } + } + } + return g +} + +type DijkstraOption func(graph *DijkstraAPSP) + +func WithPathDistsForAllNodes() DijkstraOption { + return func(graph *DijkstraAPSP) { + graph.allowed_base_ids = make(map[int]bool) + } +} + +func NewDijkstraApspFromGraph(graph *GameGraph, opts ...DijkstraOption) *DijkstraAPSP { + vertices := len(graph.matrix) + g := NewDijkstraApsp(vertices) + + index := 0 + for vertex, _ := range graph.matrix { + graph.IndexByNick[vertex] = index + graph.NicknameByIndex[index] = vertex + index++ + } + + // TODO make path reconstructions working with it + for base_nick, _ := range graph.AllowedVertixesForCalcs { + g.allowed_base_ids[graph.IndexByNick[base_nick]] = true + } + + for vertex_name, vertex := range graph.matrix { + for vertex_target, weight := range vertex { + i := graph.IndexByNick[vertex_name] + j := graph.IndexByNick[vertex_target] + + g.addEdge(i, j, int(weight)) + } + } + + for _, opt := range opts { + opt(g) + } + + return g +} + +func (g *DijkstraAPSP) addEdge(source int, destination int, weight int) { + g.adjacencyList[source] = append(g.adjacencyList[source], NewNeighbour(destination, weight)) +} + +func ArraysFill[T any](array []T, value T) { + for i := 0; i < len(array); i++ { + array[i] = value + } +} + +type Parent struct { + node int + weight int +} + +// // Time complexity of this +// // implementation of dijkstra is O(V^2). +func (g *DijkstraAPSP) dijkstra(source int) ([]int, []Parent) { + var distance []int = make([]int, g.vertices) + + // this page https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm + // helped to modify algorithm so it would return reconstructed paths. + // Parent array to store shortest + // path tree + var parents []Parent = make([]Parent, g.vertices) + // The starting vertex does not + // have a parent + for s := 0; s < g.vertices; s++ { + parents[s].node = NO_PARENT + } + + pq := make(PriorityQueue, 0) + item := &Item{ + value_weight: 0, + priority: source, + } + pq.Push(item) + + ArraysFill(distance, INF) + distance[source] = 0 + + for pq.Len() > 0 { + item := heap.Pop(&pq).(*Item) + node := item.priority + dist := item.value_weight + + for _, neighbour := range g.adjacencyList[node] { + if dist+neighbour.weight < distance[neighbour.destination] { + parents[neighbour.destination].node = node + parents[neighbour.destination].weight = neighbour.weight + distance[neighbour.destination] = dist + neighbour.weight + pq.Push(&Item{ + value_weight: distance[neighbour.destination], + priority: neighbour.destination, + }) + } + + } + + } + + // cleanup a bit + for pq.Len() > 0 { + pq.Pop() + } + + return distance, parents +} + +type DijkstraResult struct { + source int + dist_result []int + parents_result []Parent +} + +const NO_PARENT = -1 + +func (g *DijkstraAPSP) DijkstraApsp() ([][]int, [][]Parent) { + var distances [][]int = make([][]int, g.vertices) + var parents [][]Parent = make([][]Parent, g.vertices) + + // Performance optimization of the algorithm + // By skipping heaviest calculations for all shortest paths + // originiating from vertexes not needed. + // As those vertex are important only as intermediate travel point. + skip_not_allowed_vertex := func(source int) ([]int, bool) { + if len(g.allowed_base_ids) > 0 { + _, is_base := g.allowed_base_ids[source] + if !is_base { + dist := make([]int, g.vertices) + ArraysFill(dist, INF) + dist[source] = 0 + return dist, true + } + } + return nil, false + } + + // it is nice to keep sanity by keeping optional switch removing parallelism + is_sequential := false + + if is_sequential { + for s := 0; s < g.vertices; s++ { + if dist_result, is_skipped := skip_not_allowed_vertex(s); is_skipped { + distances[s] = dist_result + continue + } + dist_result, parents_result := g.dijkstra(s) + distances[s] = dist_result + parents[s] = parents_result + } + } else { + dijkstra_results := make(chan *DijkstraResult) + awaited := 0 + for s := 0; s < g.vertices; s++ { + if dist_result, is_skipped := skip_not_allowed_vertex(s); is_skipped { + distances[s] = dist_result + continue + } + awaited += 1 + go func(s int) { + dist_result, parents_result := g.dijkstra(s) + dijkstra_results <- &DijkstraResult{ + source: s, + dist_result: dist_result, + parents_result: parents_result, + } + }(s) + } + for s := 0; s < awaited; s++ { + result := <-dijkstra_results + distances[result.source] = result.dist_result + parents[result.source] = result.parents_result + } + } + + return distances, parents +} diff --git a/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/trades/graph.go b/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/trades/graph.go new file mode 100644 index 0000000..97aeb3f --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/trades/graph.go @@ -0,0 +1,193 @@ +package trades + +/* +Game graph simplifies for us conversion of data from Freelancer space simulator to different graph algorithms. +*/ + +import ( + "errors" + "math" + "reflect" + + "github.com/darklab8/fl-darkstat/configs/cfg" +) + +type VertexName string + +type GameGraph struct { + matrix map[VertexName]map[VertexName]float64 + IndexByNick map[VertexName]int `json:"index_by_nickname"` + NicknameByIndex map[int]VertexName `json:"nickname_by_index"` + AllowedVertixesForCalcs map[VertexName]bool // Consider deleting this + AvgCruiseSpeed int + idsNamesByNick map[VertexName]int + IsTradelane map[VertexName]bool + CanVisitFreightersOnlyJHs WithFreighterPaths +} + +func (g *GameGraph) WipeMatrix() { + g.matrix = nil +} + +func NewGameGraph(avgCruiseSpeed int, canVisitFreighterOnlyJHs WithFreighterPaths) *GameGraph { + return &GameGraph{ + matrix: make(map[VertexName]map[VertexName]float64), + IndexByNick: map[VertexName]int{}, + NicknameByIndex: make(map[int]VertexName), + AllowedVertixesForCalcs: make(map[VertexName]bool), + AvgCruiseSpeed: avgCruiseSpeed, + idsNamesByNick: make(map[VertexName]int), + IsTradelane: make(map[VertexName]bool), + CanVisitFreightersOnlyJHs: canVisitFreighterOnlyJHs, + } +} + +func (f *GameGraph) SetEdge(keya string, keyb string, distance float64) { + vertex, vertex_exists := f.matrix[VertexName(keya)] + if !vertex_exists { + vertex = make(map[VertexName]float64) + f.matrix[VertexName(keya)] = vertex + } + + if _, vert_target_exists := f.matrix[VertexName(keyb)]; !vert_target_exists { + f.matrix[VertexName(keyb)] = make(map[VertexName]float64) + } + + _, already_set := vertex[VertexName(keyb)] + if already_set { + return // otherwise u will overwrite tradelane distances. + } + + vertex[VertexName(keyb)] = distance +} + +func (f *GameGraph) SetIdsName(keya string, ids_name int) { + f.idsNamesByNick[VertexName(keya)] = ids_name +} + +func (f *GameGraph) SetIstRadelane(keya string) { + f.IsTradelane[VertexName(keya)] = true +} + +func GetTimeMs(f *GameGraph, dist [][]int, keya string, keyb string) (cfg.MillisecondsI, error) { + sourse_index, source_found := f.IndexByNick[VertexName(keya)] + target_index, target_found := f.IndexByNick[VertexName(keyb)] + _ = source_found + if !source_found && !target_found { + return INF, errors.New("both source and destination are not found") + } + if !source_found { + return INF, errors.New("source is not found") + } + if !target_found { + return INF, errors.New("destination is not found") + } + return dist[sourse_index][target_index], nil +} +func GetTimeMs2(f *GameGraph, dist [][]int, keya string, keyb string) cfg.MillisecondsI { + result, _ := GetTimeMs(f, dist, keya, keyb) + return result +} + +type Path struct { + Node int + NextNode int + Dist int +} + +func GetPath(graph *GameGraph, parents [][]Parent, dist [][]int, source_key string, target_key string) []Path { + // fmt.Println("get_path", source_key, target_key) + S := []Path{} + u, found_u := graph.IndexByNick[VertexName(target_key)] // target + if !found_u { + return []Path{} + } + _ = found_u + source := graph.IndexByNick[VertexName(source_key)] + + distance_skipped_buffer := 0 + + add_node := func(parent Parent) { + path_to_add := Path{ + Node: u, + } + if len(S) > 0 { + path_to_add.NextNode = S[len(S)-1].Node + } else { + path_to_add.NextNode = NO_PARENT + } + if path_to_add.Node != NO_PARENT && path_to_add.NextNode != NO_PARENT { + path_to_add.Dist = parent.weight + distance_skipped_buffer // dist[path_to_add.Node][path_to_add.NextNode] + distance_skipped_buffer = 0 + } + + S = append(S, path_to_add) + } + add_node(Parent{node: u}) + + if parents[source][u].node != NO_PARENT || u == source { + for { + parent := parents[source][u] + u = parent.node + + nickname := graph.NicknameByIndex[u] + if _, ok := graph.IsTradelane[nickname]; ok { + distance_skipped_buffer += parent.weight + continue + } + + add_node(parent) + + if u == NO_PARENT { + break + } + } + } + ReverseSlice(S) + return S +} + +// panic if s is not a slice +func ReverseSlice(s interface{}) { + size := reflect.ValueOf(s).Len() + swap := reflect.Swapper(s) + for i, j := 0, size-1; i < j; i, j = i+1, j-1 { + swap(i, j) + } +} + +type DetailedPath struct { + PrevName string + NextName string + PrevIdsName int + NextIdsName int + PrevNode int + NextNode int + Dist int + TimeMinutes int + TimeSeconds int +} + +func (graph *GameGraph) GetPaths(parents [][]Parent, dist [][]int, source_key string, target_key string) []DetailedPath { + var detailed_paths []DetailedPath + + paths := GetPath(graph, parents, dist, source_key, target_key) + for _, path := range paths { + minutes := int(math.Floor(graph.GetTimeForDist(float64(path.Dist)) / 60)) + detailed_path := DetailedPath{ + PrevName: string(graph.NicknameByIndex[path.Node]), + NextName: string(graph.NicknameByIndex[path.NextNode]), + PrevIdsName: graph.idsNamesByNick[graph.NicknameByIndex[path.Node]], + NextIdsName: graph.idsNamesByNick[graph.NicknameByIndex[path.NextNode]], + PrevNode: path.Node, + NextNode: path.NextNode, + Dist: path.Dist, + TimeMinutes: int(minutes), + TimeSeconds: int(int(graph.GetTimeForDist(float64(path.Dist))) - minutes*60), + } + + detailed_paths = append(detailed_paths, detailed_path) + } + + return detailed_paths +} diff --git a/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/trades/heap.go b/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/trades/heap.go new file mode 100644 index 0000000..05e7c96 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/trades/heap.go @@ -0,0 +1,57 @@ +package trades + +/* +Content of this file is copy pasting somewhere from https://pkg.go.dev/container/heap +*/ + +import ( + "container/heap" +) + +// An Item is something we manage in a priority queue. +type Item struct { + value_weight int // The value of the item; arbitrary. + priority int // The priority of the item in the queue. + // The index is needed by update and is maintained by the heap.Interface methods. + index int // The index of the item in the heap. +} + +// A PriorityQueue implements heap.Interface and holds Items. +type PriorityQueue []*Item + +func (pq PriorityQueue) Len() int { return len(pq) } + +func (pq PriorityQueue) Less(i, j int) bool { + // We want Pop to give us the highest, not lowest, priority so we use greater than here. + return pq[i].priority > pq[j].priority +} + +func (pq PriorityQueue) Swap(i, j int) { + pq[i], pq[j] = pq[j], pq[i] + pq[i].index = i + pq[j].index = j +} + +func (pq *PriorityQueue) Push(x any) { + n := len(*pq) + item := x.(*Item) + item.index = n + *pq = append(*pq, item) +} + +func (pq *PriorityQueue) Pop() any { + old := *pq + n := len(old) + item := old[n-1] + old[n-1] = nil // avoid memory leak + item.index = -1 // for safety + *pq = old[0 : n-1] + return item +} + +// update modifies the priority and value of an Item in the queue. +func (pq *PriorityQueue) update(item *Item, value int, priority int) { + item.value_weight = value + item.priority = priority + heap.Fix(pq, item.index) +} diff --git a/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/trades/trade_routes.go b/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/trades/trade_routes.go new file mode 100644 index 0000000..719d3ed --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/darkstat/configs_export/trades/trade_routes.go @@ -0,0 +1,379 @@ +package trades + +import ( + "math" + "strings" + + "github.com/darklab8/fl-darkstat/configs/cfg" + "github.com/darklab8/fl-darkstat/configs/configs_mapped" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/initialworld/flhash" + "github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/universe_mapped/systems_mapped" + "github.com/darklab8/fl-darkstat/darkstat/settings" + "github.com/darklab8/go-utils/utils/ptr" +) + +type SystemObject struct { + nickname string + pos cfg.Vector +} + +func DistanceForVecs(Pos1 cfg.Vector, Pos2 cfg.Vector) float64 { + // if _, ok := Pos1.X.GetValue(); !ok { + // return 0, errors.New("no x") + // } + // if _, ok := Pos2.X.GetValue(); !ok { + // return 0, errors.New("no x") + // } + + x_dist := math.Pow((Pos1.X - Pos2.X), 2) + y_dist := math.Pow((Pos1.Y - Pos2.Y), 2) + z_dist := math.Pow((Pos1.Z - Pos2.Z), 2) + distance := math.Pow((x_dist + y_dist + z_dist), 0.5) + return distance +} + +type WithFreighterPaths bool + +type RouteShipType int64 + +const ( + RouteTransport RouteShipType = iota + RouteFrigate + RouteFreighter +) + +type ShipSpeeds struct { + AvgTransportCruiseSpeed int + AvgFrigateCruiseSpeed int + AvgFreighterCruiseSpeed int +} + +var VanillaSpeeds ShipSpeeds = ShipSpeeds{ + AvgTransportCruiseSpeed: 350, + AvgFrigateCruiseSpeed: 350, + AvgFreighterCruiseSpeed: 350, +} + +var DiscoverySpeeds ShipSpeeds = ShipSpeeds{ + AvgTransportCruiseSpeed: 350, // TODO You should grab those speeds from some ship example + AvgFrigateCruiseSpeed: 500, // TODO You should grab those speeds from some ship example + AvgFreighterCruiseSpeed: 500, // TODO You should grab those speeds from some ship example +} + +var FLSRSpeeds ShipSpeeds = ShipSpeeds{ + AvgTransportCruiseSpeed: 500, // TODO You should grab those speeds from some ship example + AvgFrigateCruiseSpeed: 500, // TODO You should grab those speeds from some ship example + AvgFreighterCruiseSpeed: 500, // TODO You should grab those speeds from some ship example +} + +const ( + // already accounted for + + // Add for every pair of jumphole in path + JumpHoleDelaySec = 15 // and jump gate + // add for every tradelane vertex pair in path + TradeLaneDockingDelaySec = 10 + // add just once + BaseDockingDelay = 10 +) + +type ExtraBase struct { + Pos cfg.Vector + Nickname cfg.BaseUniNick +} + +/* +Algorithm should be like this: +We iterate through list of Systems: +Adding all bases, jump gates, jump holes, tradelanes as Vertexes. +We scan in advance nicknames for object on another side of jump gate/hole and add it as vertix +We calculcate distances between them. Distance between jump connections is 0 (or time to wait measured in distance) +We calculate distances between trade lanes as shorter than real distance for obvious reasons. +The matrix built on a fight run will be having connections between vertixes as hashmaps of possible edges? For optimized memory consumption in a sparse matrix. + +Then on second run, knowing amount of vertixes +We build Floyd matrix? With allocating memory in bulk it should be rather rapid may be. +And run Floud algorithm. +Thus we have stuff calculated for distances between all possible trading locations. (edited) +[6:02 PM] +==== +Then we build table of Bases as starting points. +And on click we show proffits of delivery to some location. With time of delivery. And profit per time. +[6:02 PM] +==== +Optionally print sum of two best routes that can be started within close range from each other. +*/ +type MappingOptions struct { + TradeRoutesDetailedTradeLane *bool +} + +func MapConfigsToFGraph( + configs *configs_mapped.MappedConfigs, + avgCruiseSpeed int, + with_freighter_paths WithFreighterPaths, + extra_bases_by_system map[string][]ExtraBase, + opts MappingOptions, +) *GameGraph { + if opts.TradeRoutesDetailedTradeLane == nil { + opts.TradeRoutesDetailedTradeLane = ptr.Ptr(settings.Env.TradeRoutesDetailedTradeLane) + } + average_trade_lane_speed := configs.GetAvgTradeLaneSpeed() + + graph := NewGameGraph(avgCruiseSpeed, with_freighter_paths) + for _, system := range configs.Systems.Systems { + system_speed_multiplier := configs.Overrides.GetSystemSpeedMultiplier(system.Nickname) + + var system_objects []SystemObject = make([]SystemObject, 0, 50) + + if bases, ok := extra_bases_by_system[system.Nickname]; ok { + for _, base := range bases { + object := SystemObject{ + nickname: base.Nickname.ToStr(), + pos: base.Pos, + } + graph.SetIdsName(object.nickname, int(flhash.HashNickname(object.nickname))) + + for _, existing_object := range system_objects { + distance := graph.DistanceToTime( + DistanceForVecs(object.pos, existing_object.pos), + system_speed_multiplier, + ) + BaseDockingDelay*PrecisionMultipiler + graph.SetEdge(object.nickname, existing_object.nickname, distance) + graph.SetEdge(existing_object.nickname, object.nickname, distance) + } + + graph.AllowedVertixesForCalcs[VertexName(object.nickname)] = true + + system_objects = append(system_objects, object) + } + } + + for _, system_obj := range system.Bases { + // system_base_base := system_obj.Base.Get() + system_base_base, dockable := system_obj.DockWith.GetValue() + + if !dockable { + continue + } + object := SystemObject{ + nickname: system_base_base, + pos: system_obj.Pos.Get(), + } + graph.SetIdsName(object.nickname, system_obj.IdsName.Get()) + + if system_obj.Archetype.Get() == systems_mapped.BaseArchetypeInvisible { + continue + } + + object_nickname := system_obj.Nickname.Get() + if _, ok := configs.InitialWorld.LockedGates[flhash.HashNickname(object_nickname)]; ok { + continue + } + + // get all objects with same Base? + // Check if any of them has docking sphere medium + + if configs.Discovery != nil { + is_dockable_by_transports := false + if bases, ok := system.AllBasesByDockWith[system_base_base]; ok { + for _, base_obj := range bases { + base_archetype := base_obj.Archetype.Get() + if solar, ok := configs.Solararch.SolarsByNick[base_archetype]; ok { + if solar.IsDockableByCaps() { + is_dockable_by_transports = true + } + } + } + } + if !is_dockable_by_transports && bool(!with_freighter_paths) { + continue + } + } + + // Lets allow flying between all bases + // goods, goods_defined := configs.Market.GoodsPerBase[object.nickname] + // if !goods_defined { + // continue + // } + + // if len(goods.MarketGoods) == 0 { + // continue + // } + + for _, existing_object := range system_objects { + distance := graph.DistanceToTime( + DistanceForVecs(object.pos, existing_object.pos), + system_speed_multiplier, + ) + BaseDockingDelay*PrecisionMultipiler + graph.SetEdge(object.nickname, existing_object.nickname, distance) + graph.SetEdge(existing_object.nickname, object.nickname, distance) + } + + graph.AllowedVertixesForCalcs[VertexName(object.nickname)] = true + + system_objects = append(system_objects, object) + } + + for _, jumphole := range system.Jumpholes { + object := SystemObject{ + nickname: jumphole.Nickname.Get(), + pos: jumphole.Pos.Get(), + } + graph.SetIdsName(object.nickname, jumphole.IdsName.Get()) + + jh_archetype := jumphole.Archetype.Get() + + // Check Solar if this is Dockable + if solar, ok := configs.Solararch.SolarsByNick[jh_archetype]; ok { + if len(solar.DockingSpheres) == 0 { + continue + } + } + + // Check locked_gate if it is enterable. + hash_id := flhash.HashNickname(object.nickname) + if _, ok := configs.InitialWorld.LockedGates[hash_id]; ok { + continue + } + + if strings.Contains(jh_archetype, "invisible") { + continue + } + + if configs.Discovery != nil { + is_dockable_by_transports := false + if solar, ok := configs.Solararch.SolarsByNick[jh_archetype]; ok { + // strings.Contains(jh_archetype, "_fighter") || // Atmospheric entry points. Dockable only by fighters/freighters + // included into `IsDockableByCaps` as they don't have capital docking_sphere dockings + if solar.IsDockableByCaps() { + is_dockable_by_transports = true + } + } + + // Condition is initiallly taken from FLCompanion + // https://github.com/Corran-Raisu/FLCompanion/blob/021159e3b3a1b40188c93064f1db136780424ea9/Datas.cpp#L585 + // but then rewritted to docking_sphere checks. + // only with docking_sphere =jump, moor_large we can dock in disco by transports + if strings.Contains(jh_archetype, "_notransport") { // jumphole_notransport Dockable only by ships with below 650 cargo on board + // "dsy_hypergate_all" is one directional hypergate dockable by everything, no need to exclude for freighter only paths + is_dockable_by_transports = false + } + if !is_dockable_by_transports && bool(!with_freighter_paths) { + continue + } + } + + for _, existing_object := range system_objects { + distance := graph.DistanceToTime(DistanceForVecs(object.pos, existing_object.pos), + system_speed_multiplier, + ) + JumpHoleDelaySec*PrecisionMultipiler + graph.SetEdge(object.nickname, existing_object.nickname, distance) + graph.SetEdge(existing_object.nickname, object.nickname, distance) + } + + jumphole_target_hole := jumphole.GotoHole.Get() + graph.SetEdge(object.nickname, jumphole_target_hole, 0) + system_objects = append(system_objects, object) + } + + for _, tradelane := range system.Tradelanes { + object := SystemObject{ + nickname: tradelane.Nickname.Get(), + pos: tradelane.Pos.Get(), + } + graph.SetIstRadelane(object.nickname) + + next_tradelane, next_exists := tradelane.NextRing.GetValue() + prev_tradelane, prev_exists := tradelane.PrevRing.GetValue() + + if *opts.TradeRoutesDetailedTradeLane { + // in production every trade lane ring will work as separate entity + // CONSUMES A LOT OF RAM MEMORY. + if next_exists { + if last_tradelane, ok := system.TradelaneByNick[next_tradelane]; ok { + distance := DistanceForVecs(object.pos, last_tradelane.Pos.Get()) + distance_inside_tradelane := distance * PrecisionMultipiler / float64(average_trade_lane_speed) + graph.SetEdge(object.nickname, last_tradelane.Nickname.Get(), distance_inside_tradelane) + } + } + + if prev_exists { + if last_tradelane, ok := system.TradelaneByNick[prev_tradelane]; ok { + distance := DistanceForVecs(object.pos, last_tradelane.Pos.Get()) + distance_inside_tradelane := distance * PrecisionMultipiler / float64(average_trade_lane_speed) + graph.SetEdge(object.nickname, last_tradelane.Nickname.Get(), distance_inside_tradelane) + } + } + } else { + // for dev env purposes to speed up test execution, we treat tradelanes as single entity + // THIS CONSUMES FAR LESS RAM MEMORY. For this reason making it default. + if next_exists && prev_exists { + continue + } + + // next or previous tradelane + chained_tradelane := "" + if next_exists { + chained_tradelane = next_tradelane + } else { + chained_tradelane = prev_tradelane + } + var last_tradelane *systems_mapped.TradeLaneRing + // iterate to last in a chain + for { + another_tradelane, ok := system.TradelaneByNick[chained_tradelane] + if !ok { + break + } + last_tradelane = another_tradelane + + if next_exists { + chained_tradelane, _ = another_tradelane.NextRing.GetValue() + } else { + chained_tradelane, _ = another_tradelane.PrevRing.GetValue() + } + if chained_tradelane == "" { + break + } + } + + if last_tradelane == nil { + continue + } + distance := DistanceForVecs(object.pos, last_tradelane.Pos.Get()) + distance_inside_tradelane := distance * PrecisionMultipiler / float64(average_trade_lane_speed) + graph.SetEdge(object.nickname, last_tradelane.Nickname.Get(), distance_inside_tradelane) + } + + for _, existing_object := range system_objects { + distance := graph.DistanceToTime( + DistanceForVecs(object.pos, existing_object.pos), + system_speed_multiplier, + ) + TradeLaneDockingDelaySec*PrecisionMultipiler + graph.SetEdge(object.nickname, existing_object.nickname, distance) + graph.SetEdge(existing_object.nickname, object.nickname, distance) + } + + system_objects = append(system_objects, object) + } + } + return graph +} + +// func (graph *GameGraph) GetDistForTime(time int) float64 { +// return float64(time * graph.AvgCruiseSpeed) +// } + +func (graph *GameGraph) DistanceToTime(distance float64, system_speed_multiplier float64) cfg.Milliseconds { + // we assume graph.AvgCruiseSpeed is above zero smth. Not going to check correctness + // lets try in milliseconds + return distance * float64(PrecisionMultipiler) / (float64(graph.AvgCruiseSpeed) * system_speed_multiplier) +} + +func (graph *GameGraph) GetTimeForDist(dist cfg.Milliseconds) cfg.Seconds { + // Surprise ;) Distance is time now. + return dist / PrecisionMultipiler +} + +// makes time in ms. Higher int value help having better calcs. +const PrecisionMultipiler = cfg.Milliseconds(1000) diff --git a/vendor/github.com/darklab8/fl-darkstat/darkstat/front/static/files/docs_coordinates_in_trade_routes.png b/vendor/github.com/darklab8/fl-darkstat/darkstat/front/static/files/docs_coordinates_in_trade_routes.png new file mode 100644 index 0000000..ea13ef2 Binary files /dev/null and b/vendor/github.com/darklab8/fl-darkstat/darkstat/front/static/files/docs_coordinates_in_trade_routes.png differ diff --git a/vendor/github.com/darklab8/fl-darkstat/darkstat/front/static/files/docs_grabbing_hptype.png b/vendor/github.com/darklab8/fl-darkstat/darkstat/front/static/files/docs_grabbing_hptype.png new file mode 100644 index 0000000..be71cf6 Binary files /dev/null and b/vendor/github.com/darklab8/fl-darkstat/darkstat/front/static/files/docs_grabbing_hptype.png differ diff --git a/vendor/github.com/darklab8/fl-darkstat/darkstat/front/static/files/docs_infocard_search.png b/vendor/github.com/darklab8/fl-darkstat/darkstat/front/static/files/docs_infocard_search.png new file mode 100644 index 0000000..a48c3cf Binary files /dev/null and b/vendor/github.com/darklab8/fl-darkstat/darkstat/front/static/files/docs_infocard_search.png differ diff --git a/vendor/github.com/darklab8/fl-darkstat/darkstat/front/static/files/docs_movable_borders.png b/vendor/github.com/darklab8/fl-darkstat/darkstat/front/static/files/docs_movable_borders.png new file mode 100644 index 0000000..92fedb7 Binary files /dev/null and b/vendor/github.com/darklab8/fl-darkstat/darkstat/front/static/files/docs_movable_borders.png differ diff --git a/vendor/github.com/darklab8/fl-darkstat/darkstat/front/static/files/docs_not_for_transports.png b/vendor/github.com/darklab8/fl-darkstat/darkstat/front/static/files/docs_not_for_transports.png new file mode 100644 index 0000000..0dd0883 Binary files /dev/null and b/vendor/github.com/darklab8/fl-darkstat/darkstat/front/static/files/docs_not_for_transports.png differ diff --git a/vendor/github.com/darklab8/fl-darkstat/darkstat/front/static/files/docs_ordering.png b/vendor/github.com/darklab8/fl-darkstat/darkstat/front/static/files/docs_ordering.png new file mode 100644 index 0000000..f00cc52 Binary files /dev/null and b/vendor/github.com/darklab8/fl-darkstat/darkstat/front/static/files/docs_ordering.png differ diff --git a/vendor/github.com/darklab8/fl-darkstat/darkstat/front/static/files/docs_pinning.png b/vendor/github.com/darklab8/fl-darkstat/darkstat/front/static/files/docs_pinning.png new file mode 100644 index 0000000..f809a3b Binary files /dev/null and b/vendor/github.com/darklab8/fl-darkstat/darkstat/front/static/files/docs_pinning.png differ diff --git a/vendor/github.com/darklab8/fl-darkstat/darkstat/front/static/files/docs_seaching_for_hp_type.png b/vendor/github.com/darklab8/fl-darkstat/darkstat/front/static/files/docs_seaching_for_hp_type.png new file mode 100644 index 0000000..1ebfdd2 Binary files /dev/null and b/vendor/github.com/darklab8/fl-darkstat/darkstat/front/static/files/docs_seaching_for_hp_type.png differ diff --git a/vendor/github.com/darklab8/fl-darkstat/darkstat/front/static/files/docs_search_bar.png b/vendor/github.com/darklab8/fl-darkstat/darkstat/front/static/files/docs_search_bar.png new file mode 100644 index 0000000..d04c877 Binary files /dev/null and b/vendor/github.com/darklab8/fl-darkstat/darkstat/front/static/files/docs_search_bar.png differ diff --git a/vendor/github.com/darklab8/fl-darkstat/darkstat/front/static/files/docs_tech_compat_id_selector.png b/vendor/github.com/darklab8/fl-darkstat/darkstat/front/static/files/docs_tech_compat_id_selector.png new file mode 100644 index 0000000..8d965b9 Binary files /dev/null and b/vendor/github.com/darklab8/fl-darkstat/darkstat/front/static/files/docs_tech_compat_id_selector.png differ diff --git a/vendor/github.com/darklab8/fl-darkstat/darkstat/front/static/files/docs_techcompat_hover.png b/vendor/github.com/darklab8/fl-darkstat/darkstat/front/static/files/docs_techcompat_hover.png new file mode 100644 index 0000000..e6a3e9d Binary files /dev/null and b/vendor/github.com/darklab8/fl-darkstat/darkstat/front/static/files/docs_techcompat_hover.png differ diff --git a/vendor/github.com/darklab8/fl-darkstat/darkstat/front/static/files/docs_timestamp.png b/vendor/github.com/darklab8/fl-darkstat/darkstat/front/static/files/docs_timestamp.png new file mode 100644 index 0000000..d60b9eb Binary files /dev/null and b/vendor/github.com/darklab8/fl-darkstat/darkstat/front/static/files/docs_timestamp.png differ diff --git a/vendor/github.com/darklab8/fl-darkstat/darkstat/front/static/files/pob_craft_enhanced_infocard.png b/vendor/github.com/darklab8/fl-darkstat/darkstat/front/static/files/pob_craft_enhanced_infocard.png new file mode 100644 index 0000000..c4dd255 Binary files /dev/null and b/vendor/github.com/darklab8/fl-darkstat/darkstat/front/static/files/pob_craft_enhanced_infocard.png differ diff --git a/vendor/github.com/darklab8/fl-darkstat/darkstat/front/static/files/pob_crafts_base.png b/vendor/github.com/darklab8/fl-darkstat/darkstat/front/static/files/pob_crafts_base.png new file mode 100644 index 0000000..4a7793e Binary files /dev/null and b/vendor/github.com/darklab8/fl-darkstat/darkstat/front/static/files/pob_crafts_base.png differ diff --git a/vendor/github.com/darklab8/fl-darkstat/darkstat/front/static/files/pob_goods_search.png b/vendor/github.com/darklab8/fl-darkstat/darkstat/front/static/files/pob_goods_search.png new file mode 100644 index 0000000..7eb37f9 Binary files /dev/null and b/vendor/github.com/darklab8/fl-darkstat/darkstat/front/static/files/pob_goods_search.png differ diff --git a/vendor/github.com/darklab8/fl-darkstat/darkstat/front/static/files/pobs_forun_account.png b/vendor/github.com/darklab8/fl-darkstat/darkstat/front/static/files/pobs_forun_account.png new file mode 100644 index 0000000..fec7039 Binary files /dev/null and b/vendor/github.com/darklab8/fl-darkstat/darkstat/front/static/files/pobs_forun_account.png differ diff --git a/vendor/github.com/darklab8/fl-darkstat/darkstat/front/static/files/pobs_in_commodities.png b/vendor/github.com/darklab8/fl-darkstat/darkstat/front/static/files/pobs_in_commodities.png new file mode 100644 index 0000000..45a31ca Binary files /dev/null and b/vendor/github.com/darklab8/fl-darkstat/darkstat/front/static/files/pobs_in_commodities.png differ diff --git a/vendor/github.com/darklab8/fl-darkstat/darkstat/front/static/files/pobs_pob_tab.png b/vendor/github.com/darklab8/fl-darkstat/darkstat/front/static/files/pobs_pob_tab.png new file mode 100644 index 0000000..4f82052 Binary files /dev/null and b/vendor/github.com/darklab8/fl-darkstat/darkstat/front/static/files/pobs_pob_tab.png differ diff --git a/vendor/github.com/darklab8/fl-darkstat/darkstat/front/static/static.go b/vendor/github.com/darklab8/fl-darkstat/darkstat/front/static/static.go new file mode 100644 index 0000000..7157559 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/darkstat/front/static/static.go @@ -0,0 +1,19 @@ +package static + +import ( + "embed" + + "github.com/darklab8/fl-darkstat/darkcore/core_front" + "github.com/darklab8/go-utils/utils/utils_types" +) + +//go:embed files/* +var currentdir embed.FS + +var StaticFilesystem core_front.StaticFilesystem = core_front.GetFiles( + currentdir, + utils_types.GetFilesParams{RootFolder: utils_types.FilePath("files")}, +) + +// Example how to import on init +// var PictureCoordinatesInTradeRoutes core_types.StaticFile = StaticFilesystem.GetFileByRelPath("docs_coordinates_in_trade_routes.png") diff --git a/vendor/github.com/darklab8/fl-darkstat/darkstat/front/static_front/common.css b/vendor/github.com/darklab8/fl-darkstat/darkstat/front/static_front/common.css new file mode 100644 index 0000000..44dee80 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/darkstat/front/static_front/common.css @@ -0,0 +1,29 @@ +/*Defaults*/ +h3 { + font-size: 1.4em; + font-weight: bold; +} + +p { + font-size: 1.1em; +} + +hr { + display: block; + border-top: 1px solid #ccc; + width: 100%; + height: 0px; + border-color: var(--color_border); +} + +hr, +div, +table { + margin: 0px; + padding: 0px; +} + +b, +.bold { + font-weight: bold; +} \ No newline at end of file diff --git a/vendor/github.com/darklab8/fl-darkstat/darkstat/front/static_front/custom.css b/vendor/github.com/darklab8/fl-darkstat/darkstat/front/static_front/custom.css new file mode 100644 index 0000000..7ac282c --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/darkstat/front/static_front/custom.css @@ -0,0 +1,69 @@ +/* Tooltip container */ +.tooltip { + display: inline-block; + position: relative; +} + +/* Tooltip text */ +.tooltip .tooltiptext { + visibility: hidden; + background-color: black; + color: #fff; + text-align: left; + padding: 5px; + border-radius: 6px; + + /* Position the tooltip text - see examples below! */ + position: absolute; + z-index: 1; + + opacity: 0; + transition: 0.3s; + transition-delay: 0.3s; +} + +/* Show the tooltip text when you mouse over the tooltip container */ +.tooltip:hover .tooltiptext { + visibility: visible; + opacity: 1; + z-index: 30; +} + +.vertical_th { + display: flex; + flex-direction: column; +} + +.vertical_wrap { + display: flex; + flex-direction: row; + flex-wrap: wrap; + justify-content: center; + align-items: center; + align-content: center; + width: 100%; + height: 100%; +} + +.vertical_wrap span {} + + +highlight { + background-color: var(--infocard_text_highlight); +} + +.magni_glass { + opacity: 0.45; +} + +th { + z-index: 1; +} + +.line_through { + text-decoration: line-through; +} + +.line_underline { + text-decoration: underline; +} \ No newline at end of file diff --git a/vendor/github.com/darklab8/fl-darkstat/darkstat/front/static_front/custom/filter_route_min_dists.js b/vendor/github.com/darklab8/fl-darkstat/darkstat/front/static_front/custom/filter_route_min_dists.js new file mode 100644 index 0000000..0d4d18c --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/darkstat/front/static_front/custom/filter_route_min_dists.js @@ -0,0 +1,104 @@ +/* +How to implement? +Insert json with data of routes into every row. Right into cell needing recalculation ;) +[{"time": smth, "profit": "smth"}], format like this + +In Input Value Change + Grab input value. + Per each ship category: + Find max proffit per distance + Which have required minimum distance. + Insert updated to the Cell + +On Render: + Grab Input Value + Hide Rows Which have distances in all Three cells less than minimum. + +P.S. how to make it playing nice with other filters? mm... check some flag if it was already filtered by smth else. +U can https://stackoverflow.com/questions/4258466/can-i-add-arbitrary-properties-to-dom-objects +*/ + + +function FilteringForDistances() { // eslint-disable-line no-unused-vars + // Declare variables + let input, table, tr, max_profit; + + input = document.getElementById("input_route_min_dist"); + let min_distance_threshold = input.value; + if (min_distance_threshold === '') { + min_distance_threshold = 0 + } + + table = document.querySelector("#table-top table"); + tr = table.getElementsByTagName("tr"); + + // Loop through all table rows, and hide those who don't match the search query + for (let i = 1; i < tr.length; i++) { + let row = tr[i]; + + for (let r = 0; r < route_types.length; r++) { // eslint-disable-line no-undef + let cell = row.getElementsByClassName(route_types[r])[0]; // eslint-disable-line no-undef + + let routesinfo = JSON.parse(cell.attributes["routesinfo"].textContent); + + if (routesinfo === null) { + continue + } + // list of { ProffitPetTime TotalSeconds } number values + // renamed to { p s } for client side not overloading reasons. otherwise html was taking 155mb + max_profit = 0 + for (let j = 0; j < routesinfo.length; j++) { + if (routesinfo[j].S > min_distance_threshold) { + if (routesinfo[j].P > max_profit) { + max_profit = routesinfo[j].P + } + } + } + + cell.innerHTML = (100 * max_profit).toFixed(2); + } + } +} + +function FilteringForDistAfterRender() { // eslint-disable-line no-unused-vars + let maximum_time_for_row, table, tr, min_distance_threshold + + table = document.querySelector("#table-bottom-main") + if (table === null || typeof (table) == 'undefined') { + return + } + tr = table.getElementsByTagName("tr"); + let input = document.getElementById("input_route_min_dist"); + min_distance_threshold = input.value; + if (min_distance_threshold === '') { + min_distance_threshold = 0 + } + + for (let i = 1; i < tr.length; i++) { + let row = tr[i]; + + if (IsHavingLocksFromOtherFilters(row, 'darkstat_filtering2')) { // eslint-disable-line no-undef + continue + } + + maximum_time_for_row = 0 + // Find maximum time + for (let r = 0; r < route_types.length; r++) { // eslint-disable-line no-undef + let cell = row.getElementsByClassName(route_types[r])[0]; // eslint-disable-line no-undef + + if (Number(cell.attributes["routetime"].textContent) > Number(maximum_time_for_row)) { + maximum_time_for_row = cell.attributes["routetime"].textContent + } + } + + if (Number(maximum_time_for_row) < Number(min_distance_threshold)) { + tr[i].style.display = "none"; + row.darkstat_filtering2 = true + } else { + tr[i].style.display = ""; + if ('darkstat_filtering2' in row) { + delete row.darkstat_filtering2 + } + } + } +} diff --git a/vendor/github.com/darklab8/fl-darkstat/darkstat/front/static_front/custom/filtering.js b/vendor/github.com/darklab8/fl-darkstat/darkstat/front/static_front/custom/filtering.js new file mode 100644 index 0000000..f594a2d --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/darkstat/front/static_front/custom/filtering.js @@ -0,0 +1,173 @@ +/** + * Implements functionality for filtering search bar + * For table that has also filtering by selected ID tech compatibility, which is needed for Freelancer Discovery + */ +function FilteringFunction() { // eslint-disable-line no-unused-vars + // Declare variables + // console.log("triggered FilteringFunction") + let input, filter, filter_infocard, table, tr, txtValue, txtValue_infocard; + input = document.getElementById("filterinput"); + if (typeof (input) === 'undefined' || input === null) { + return; + } + let input_infocard = document.getElementById("filterinput_infocard"); + filter_infocard = input_infocard.value.toUpperCase(); + filter = input.value.toUpperCase(); + table = document.querySelector("#table-top table"); + tr = table.getElementsByTagName("tr"); + + // Select current ID tractor + let tractor_id_elem, tractor_id_selected; + tractor_id_selected = ""; + tractor_id_elem = document.getElementById("tractor_id_selector"); + if (typeof (tractor_id_elem) != 'undefined' && tractor_id_elem != null) { + tractor_id_selected = tractor_id_elem.value; + + sessionStorage.setItem("tractor_id_selected_index", tractor_id_elem.selectedIndex); + + } + + // making invisible info about ID Compatibility if no ID is selected + if (tractor_id_selected === "") { + let row = tr[0]; + let cell = row.getElementsByClassName("tech_compat")[0]; + if (typeof (cell) != 'undefined') { + cell.style.display = "none"; + } + + } else { + let row = tr[0]; + let cell = row.getElementsByClassName("tech_compat")[0]; + if (typeof (cell) != 'undefined') { + cell.style.display = ""; + } + + } + + // Loop through all table rows, and hide those who don't match the search query + for (let i = 1; i < tr.length; i++) { + // row = document.getElementById("bottominfo_dsy_councilhf") + let row = tr[i]; + + let txtValues = [] + let tds = row.getElementsByClassName("seo") + for (let elem of tds) { + let value = elem.textContent || elem.innerText; + txtValues.push(value) + } + txtValue = txtValues.join(''); + + let infocards = row.getElementsByClassName("search-infocard"); + txtValue_infocard = ''; + if (infocards.length > 0) { + txtValue_infocard = infocards[0].textContent || infocards[0].innerText + } + + // Refresh tech compat value + let techcompat_visible = true; + let compatibility; + let cell = row.getElementsByClassName("tech_compat")[0]; + if (typeof (cell) != 'undefined') { + let techcompats = JSON.parse(cell.attributes["techcompats"].textContent.replaceAll("'", '"')); + + if (tractor_id_selected in techcompats) { + compatibility = techcompats[tractor_id_selected] * 100; + } else { + compatibility = 0; + } + cell.innerHTML = compatibility + "%"; + + + techcompat_visible = compatibility > 10 || tractor_id_selected === "" + + // making invisible info about ID Compatibility if no ID is selected + if (tractor_id_selected === "") { + cell.style.display = "none"; + } else { + cell.style.display = ""; + } + + // console.log("compatibility=", compatibility, "tractor_id_selected=", tractor_id_selected, "techcompat_visible=", techcompat_visible) + } + + if ((txtValue.toUpperCase().indexOf(filter) > -1 && txtValue_infocard.toUpperCase().indexOf(filter_infocard) > -1) && techcompat_visible === true) { + tr[i].style.display = ""; + // console.log("row-i", i, "is made visible"); + } else { + tr[i].style.display = "none"; + // console.log("row-i", i, "is made invisible"); + } + } +} + +/** + * Implements functionality for filtering search bar + * @param {string} table_selector + * @param {string} input_selector + */ +function FilteringForAnyTable(table_selector, input_selector) { // eslint-disable-line no-unused-vars + // Declare variables + // console.log("triggered FilteringFunction") + let input, filter, table, tr, txtValue; + input = document.getElementById(input_selector); // "filterinput" + filter = input.value.toUpperCase(); + table = document.querySelector(table_selector); // "#table-top table" + tr = table.getElementsByTagName("tr"); + + + // Loop through all table rows, and hide those who don't match the search query + for (let i = 1; i < tr.length; i++) { + let row = tr[i]; + txtValue = row.textContent || row.innerText; + + if (IsHavingLocksFromOtherFilters(row, 'darkstat_filtering1')) { // eslint-disable-line no-undef + continue + } + + if (txtValue.toUpperCase().indexOf(filter) > -1) { + tr[i].style.display = ""; + if ('darkstat_filtering1' in row) { + delete row.darkstat_filtering1 + } + // console.log("row-i", i, "is made visible"); + } else { + tr[i].style.display = "none"; + row.darkstat_filtering1 = true + // console.log("row-i", i, "is made invisible"); + } + } +} + +/** + * Useful to highlight searched text in an infocard + * @param {HTMLElement} inputText + * @param {HTMLElement} text + */ +function highlight(input_infocard, infocard) { + let innerHTML = infocard.innerHTML; + + innerHTML = innerHTML.replaceAll("", ""); + innerHTML = innerHTML.replaceAll("", ""); + document.getElementsByClassName("infocard")[0].innerHTML = innerHTML + + // let index = infocard.innerHTML.toUpperCase().indexOf(input_infocard.value.toUpperCase()); + + if (input_infocard.value.length < 2) { + return + } + + var searchMask = input_infocard.value; + var regEx = new RegExp(searchMask, "ig"); + var replaceMask = "" + input_infocard.value + ""; + innerHTML = innerHTML.replace(regEx, replaceMask); + document.getElementsByClassName("infocard")[0].innerHTML = innerHTML +} + +function highlightInfocardHook() { // eslint-disable-line no-unused-vars + let input_infocard = document.getElementById("filterinput_infocard"); + let infocards = document.getElementsByClassName("infocard") + if (infocards.length > 0) { + let infocard = infocards[0] + highlight(input_infocard, infocard) + } +} \ No newline at end of file diff --git a/vendor/github.com/darklab8/fl-darkstat/darkstat/front/static_front/custom/main.js b/vendor/github.com/darklab8/fl-darkstat/darkstat/front/static_front/custom/main.js new file mode 100644 index 0000000..c71327e --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/darkstat/front/static_front/custom/main.js @@ -0,0 +1,103 @@ +/** + * Calculate count of visible elements in Table. + * You should probably account elements with `tr[i].style.display = "none";` too may be here. + * @param {HTMLTableElement} table + */ +function TableLen(table) { // eslint-disable-line no-unused-vars + let count = 0; + for (let row of table.rows) { + + if (!row.classList.contains(HIDDEN_CLS)) { + count = count + 1; + } + } + // console.log("count=" + count) + return count; +} + +var HIDDEN_CLS = "hidden"; + +/** + * hide, row or table or anything else + * @param {string} id + */ +function Hide(id) { // eslint-disable-line no-unused-vars + let element = document.getElementById(id); + // console.log("Hide.id=" + id) + if (!element.classList.contains(HIDDEN_CLS)) { + element.classList.add(HIDDEN_CLS); + } +} + +/** + * unhide, row or table or anything else + * @param {string} id + */ +function Unhide(id) { // eslint-disable-line no-unused-vars + let element = document.getElementById(id); + // console.log("Unhide.id=" + id) + if (element.classList.contains(HIDDEN_CLS)) { + element.classList.remove(HIDDEN_CLS); + } +} + +/** + * Function helping to persist selected ID + * when user moves across different tabs + */ +function LoadSelectedTractorID() { // eslint-disable-line no-unused-vars + // console.log("triggered LoadSelectedTractorID") + let selected_index = sessionStorage.getItem("tractor_id_selected_index"); + if (typeof (selected_index) != 'undefined' && selected_index != null) { + let tractor_id_elem = document.getElementById("tractor_id_selector"); + if (typeof (tractor_id_elem) != 'undefined' && tractor_id_elem != null) { + tractor_id_elem.selectedIndex = selected_index; + } + } +} + + + +/** + * Highlights clicked table row + * @param {HTMLTableRowElement} row + */ +function RowHighlighter(row) { // eslint-disable-line no-unused-vars + let table = row.parentElement.parentElement; + + let selected_row_id = row.rowIndex; + + let rowsNotSelected = table.getElementsByTagName('tr'); + for (let row = 0; row < rowsNotSelected.length; row++) { + rowsNotSelected[row].classList.remove('selected_row'); + } + let rowSelected = table.getElementsByTagName('tr')[selected_row_id]; + rowSelected.classList.add("selected_row"); +} + +function cloneAttributes(target, source) { + [...source.attributes].forEach(attr => { target.setAttribute(attr.nodeName, attr.nodeValue) }) +} + +function LoadTechCompat() { // eslint-disable-line no-unused-vars + console.log("loading tech compat attempt") + let targets1 = document.querySelectorAll("[data-target-1]") + for (let i = 0; i < targets1.length; i++) { + let target = targets1[i] + let cache_key = target.attributes["data-target-1"].value + let source = document.querySelector("[data-source-1='" + cache_key + "']") + target.innerHTML = source.innerHTML + cloneAttributes(target, source) + target.removeAttribute("data-target-1") + } + + let targets2 = document.querySelectorAll("[data-target-2]") + for (let i = 0; i < targets2.length; i++) { + let target = targets2[i] + let cache_key = target.attributes["data-target-2"].value + let source = document.querySelector("[data-source-2='" + cache_key + "']") + target.innerHTML = source.innerHTML + cloneAttributes(target, source) + target.removeAttribute("data-target-2") + } +} diff --git a/vendor/github.com/darklab8/fl-darkstat/darkstat/front/static_front/custom/shared.js b/vendor/github.com/darklab8/fl-darkstat/darkstat/front/static_front/custom/shared.js new file mode 100644 index 0000000..21c08a3 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/darkstat/front/static_front/custom/shared.js @@ -0,0 +1,16 @@ +/** + * Implements functionality for filtering search bar + * @param {HTMLElement} item + * @param {string} excepted_filter + */ +function IsHavingLocksFromOtherFilters(item, excepted_filter) { // eslint-disable-line no-unused-vars + const filterings = ["darkstat_filtering1", "darkstat_filtering2"]; + + for (let i = 0; i < filterings.length; i++) { + if (filterings[i] in item && excepted_filter !== filterings[i]) { + return true + } + } + + return false +} \ No newline at end of file diff --git a/vendor/github.com/darklab8/fl-darkstat/darkstat/front/static_front/custom/shared_discovery.js b/vendor/github.com/darklab8/fl-darkstat/darkstat/front/static_front/custom/shared_discovery.js new file mode 100644 index 0000000..12e4707 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/darkstat/front/static_front/custom/shared_discovery.js @@ -0,0 +1 @@ +const route_types = ["route_transport", "route_frigate", "route_freighter"]; // eslint-disable-line no-unused-vars diff --git a/vendor/github.com/darklab8/fl-darkstat/darkstat/front/static_front/custom/shared_vanilla.js b/vendor/github.com/darklab8/fl-darkstat/darkstat/front/static_front/custom/shared_vanilla.js new file mode 100644 index 0000000..9e85752 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/darkstat/front/static_front/custom/shared_vanilla.js @@ -0,0 +1 @@ +const route_types = ["route_transport"]; // eslint-disable-line no-unused-vars diff --git a/vendor/github.com/darklab8/fl-darkstat/darkstat/front/static_front/custom/table_resizer.js b/vendor/github.com/darklab8/fl-darkstat/darkstat/front/static_front/custom/table_resizer.js new file mode 100644 index 0000000..a0439ec --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/darkstat/front/static_front/custom/table_resizer.js @@ -0,0 +1,94 @@ +function makeTopBottomTablesResizable() { // eslint-disable-line no-unused-vars + const element_top = document.querySelector("#table-top") + const element_bottom = document.querySelector("#table-bottom") + const currentResizer = document.querySelector('.resizer-top-bottom') + + let top_height_perc = 0; + let botttom_height_perc = 0; + + let original_top_height = 0; + let original_botttom_height = 0; + + let top_rect_top = 0; + let bottom_rect_bottom = 0; + + currentResizer.addEventListener('mousedown', function (e) { + e.preventDefault() + + top_height_perc = 65 + if (element_top.hasOwnProperty("height_perc")) { // eslint-disable-line no-prototype-builtins + top_height_perc = element_top.height_perc + } + + botttom_height_perc = 35 + if (element_bottom.hasOwnProperty("height_perc")) { // eslint-disable-line no-prototype-builtins + botttom_height_perc = element_bottom.height_perc + } + + original_top_height = parseFloat(getComputedStyle(element_top, null).getPropertyValue('height').replace('px', '')); + original_botttom_height = parseFloat(getComputedStyle(element_bottom, null).getPropertyValue('height').replace('px', '')); + + top_rect_top = element_top.getBoundingClientRect().top; + bottom_rect_bottom = element_bottom.getBoundingClientRect().bottom; + + window.addEventListener('mousemove', resize1) + window.addEventListener('mouseup', stopResize1) + }) + + function resize1(e) { + var new_top_height = (e.pageY - top_rect_top) / original_top_height * top_height_perc + var new_bottom_height = (bottom_rect_bottom - e.pageY) / original_botttom_height * botttom_height_perc + + element_top.style.height = "calc(" + new_top_height + "% - 7px)"; + element_bottom.style.height = "calc(" + new_bottom_height + "% - 7px)"; + + element_top.height_perc = new_top_height + element_bottom.height_perc = new_bottom_height + } + + function stopResize1() { + window.removeEventListener('mousemove', resize1) + } +} + +function makeLeftRightTablesResizable() { // eslint-disable-line no-unused-vars + const currentResizer = document.querySelector('.resizer-left-right') + const element_left = document.querySelector("#table-wrapper") + const element_right = document.querySelector("#infocard_view") + + let left_width_perc = 0; + let right_width_perc = 0; + + let original_left_width = 0; + let original_right_width = 0; + + let left_rect_left = 0; + let right_rect_right = 0; + + currentResizer.addEventListener('mousedown', function (e) { + e.preventDefault() + left_width_perc = element_left.style.width.replace('%', ''); + right_width_perc = element_right.style.width.replace('%', ''); + + original_left_width = parseFloat(getComputedStyle(element_left, null).getPropertyValue('width').replace('px', '')); + original_right_width = parseFloat(getComputedStyle(element_right, null).getPropertyValue('width').replace('px', '')); + + left_rect_left = element_left.getBoundingClientRect().left; + right_rect_right = element_right.getBoundingClientRect().right; + + window.addEventListener('mousemove', resize) + window.addEventListener('mouseup', stopResize) + }) + + function resize(e) { + var new_left_width = (e.pageX - left_rect_left) / original_left_width * left_width_perc + var new_right_width = (right_rect_right - e.pageX) / original_right_width * right_width_perc + + element_left.style.width = new_left_width + "%"; + element_right.style.width = new_right_width + "%"; + } + + function stopResize() { + window.removeEventListener('mousemove', resize) + } +} \ No newline at end of file diff --git a/vendor/github.com/darklab8/fl-darkstat/darkstat/front/static_front/static.go b/vendor/github.com/darklab8/fl-darkstat/darkstat/front/static_front/static.go new file mode 100644 index 0000000..87b6f50 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/darkstat/front/static_front/static.go @@ -0,0 +1,88 @@ +package static_front + +import ( + _ "embed" + + "github.com/darklab8/fl-darkstat/darkcore/core_types" +) + +//go:embed custom/shared_vanilla.js +var CustomJSCSharedVanilla string + +var CustomJSSharedVanilla core_types.StaticFile = core_types.StaticFile{ + Content: CustomJSCSharedVanilla, + Filename: "custom/shared_vanilla.js", + Kind: core_types.StaticFileJS, +} + +//go:embed custom/shared_discovery.js +var CustomJSCSharedDiscovery string + +var CustomJSSharedDiscovery core_types.StaticFile = core_types.StaticFile{ + Content: CustomJSCSharedDiscovery, + Filename: "custom/shared_discovery.js", + Kind: core_types.StaticFileJS, +} + +//go:embed custom/shared.js +var CustomJSCShared string + +var CustomJSShared core_types.StaticFile = core_types.StaticFile{ + Content: CustomJSCShared, + Filename: "custom/shared.js", + Kind: core_types.StaticFileJS, +} + +//go:embed custom/main.js +var CustomJSContent string + +var CustomJS core_types.StaticFile = core_types.StaticFile{ + Content: CustomJSContent, + Filename: "custom/main.js", + Kind: core_types.StaticFileJS, +} + +//go:embed custom/table_resizer.js +var CustomResizerJSContent string + +var CustomJSResizer core_types.StaticFile = core_types.StaticFile{ + Content: CustomResizerJSContent, + Filename: "table_resizer.js", + Kind: core_types.StaticFileJS, +} + +//go:embed custom/filtering.js +var CustomFilteringJS string + +var CustomJSFiltering core_types.StaticFile = core_types.StaticFile{ + Content: CustomFilteringJS, + Filename: "filtering.js", + Kind: core_types.StaticFileJS, +} + +//go:embed custom/filter_route_min_dists.js +var CustomFilteringRoutesJS string + +var CustomJSFilteringRoutes core_types.StaticFile = core_types.StaticFile{ + Content: CustomFilteringRoutesJS, + Filename: "filter_route_min_dists.js", + Kind: core_types.StaticFileJS, +} + +//go:embed common.css +var CommonCSSContent string + +var CommonCSS core_types.StaticFile = core_types.StaticFile{ + Content: CommonCSSContent, + Filename: "common.css", + Kind: core_types.StaticFileCSS, +} + +//go:embed custom.css +var CustomCSSContent string + +var CustomCSS core_types.StaticFile = core_types.StaticFile{ + Content: CustomCSSContent, + Filename: "custom.css", + Kind: core_types.StaticFileCSS, +} diff --git a/vendor/github.com/darklab8/fl-darkstat/darkstat/front/tab/filterer.templ b/vendor/github.com/darklab8/fl-darkstat/darkstat/front/tab/filterer.templ new file mode 100644 index 0000000..ba8097b --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/darkstat/front/tab/filterer.templ @@ -0,0 +1,63 @@ +package tab + +import ( + "github.com/darklab8/fl-darkstat/darkstat/front/types" + "github.com/darklab8/fl-darkstat/darkstat/configs_export" +) + +templ FilterBar(shared *types.SharedData) { + +
+ + + if bool(shared.ShowDisco) { + + } + { children... } +
+} + +func GetInfocard(infocards configs_export.Infocards, infokey configs_export.InfocardKey) string { + if infocard_lines, ok := infocards[infokey]; ok { + return infocard_lines.StringsJoin("") + } + return "" +} + +templ EmbedInfocardForSearch(infocards configs_export.Infocards, infokey configs_export.InfocardKey) { + + + + +} + +templ BottomFilterBar() { + +} + diff --git a/vendor/github.com/darklab8/fl-darkstat/darkstat/front/tab/index.templ b/vendor/github.com/darklab8/fl-darkstat/darkstat/front/tab/index.templ new file mode 100644 index 0000000..6ab622f --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/darkstat/front/tab/index.templ @@ -0,0 +1,151 @@ +package tab + + +import ( + "github.com/darklab8/fl-darkstat/darkcore/core_front" + "github.com/darklab8/fl-darkstat/darkcore/core_static" + "github.com/darklab8/fl-darkstat/darkstat/front/static_front" + "github.com/darklab8/fl-darkstat/darkstat/front/types" +) + +templ Html5(theme types.Theme, shared *types.SharedData) { + + + + + @core_front.StaticFile(core_static.FaviconIco) + { "darkstat" } + + // For loading https://htmx.org/examples/tabs-hateoas/ + switch theme { + case types.ThemeDark: + + case types.ThemeVanilla: + + case types.ThemeLight: + + } + + + @core_front.StaticFile(core_static.ResetCSS) + @core_front.StaticFile(static_front.CommonCSS) + @core_front.StaticFile(static_front.CustomCSS) + + if shared.ShowDisco { + @core_front.StaticFile(static_front.CustomJSSharedDiscovery) + } else { + @core_front.StaticFile(static_front.CustomJSSharedVanilla) + } + + @core_front.StaticFile(static_front.CustomJSShared) + @core_front.StaticFile(static_front.CustomJS) + @core_front.StaticFile(static_front.CustomJSResizer) + @core_front.StaticFile(static_front.CustomJSFiltering) + @core_front.StaticFile(static_front.CustomJSFilteringRoutes) + @core_front.StaticFile(core_static.SortableJS) + @core_front.StaticFile(core_static.HtmxJS) + @core_front.StaticFile(core_static.HtmxPreloadJS) + + + + { children... } + + +} diff --git a/vendor/github.com/darklab8/fl-darkstat/darkstat/front/tab/infocard.templ b/vendor/github.com/darklab8/fl-darkstat/darkstat/front/tab/infocard.templ new file mode 100644 index 0000000..45c67c9 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/darkstat/front/tab/infocard.templ @@ -0,0 +1,43 @@ +package tab + +import ( + "github.com/darklab8/fl-darkstat/darkstat/configs_export" + "strings" + "html" +) + +templ InfocardShared() { + +
+

infocard

+ { children... } +
+} + +templ Infocard(info configs_export.Infocard) { + @InfocardShared() { + for _, line := range info { + if strings.ReplaceAll(line.ToStr()," ", "") == "" { +


+ } else { +

+ for _, phrase := range line.Phrases { + if phrase.Link != nil { + { html.UnescapeString(phrase.Phrase) } + } else { + { html.UnescapeString(phrase.Phrase) } + } + } +

+ } + } + } +} diff --git a/vendor/github.com/darklab8/fl-darkstat/darkstat/front/tab/menu.templ b/vendor/github.com/darklab8/fl-darkstat/darkstat/front/tab/menu.templ new file mode 100644 index 0000000..bbb778c --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/darkstat/front/tab/menu.templ @@ -0,0 +1,235 @@ +package tab + +import ( + "github.com/darklab8/fl-darkstat/darkstat/front/types" + "github.com/darklab8/fl-darkstat/darkstat/front/urls" + "github.com/darklab8/fl-darkstat/darkstat/settings" + "github.com/darklab8/fl-darkstat/darkstat/front/frmt" + "github.com/darklab8/go-utils/utils/utils_types" + "strings" + "context" +) + +templ Heading(shared *types.SharedData) { + +
+
+ { children... } +
+
+ if shared.ShowDisco { + + } +
+ Darkstat { settings.Env.AppVersion } refreshed at { types.GetCtx(ctx).Timestamp.Format("02 Jan 2006 15:04:05") } UTC +
+
+ @templ.Raw(types.GetCtx(ctx).Heading) +
+
+
+} + +templ TabStyle() { + +} + +script GoToUrl(current string, themes []string) { + let index = 0; + + for (let i=0, item; item = themes[i]; i++) { + // Look no need to do list[i] in the body of the loop + console.log("Looping: index ", i, "item" + item); + if (location.pathname == item) { + index = i; + } + } + + index = index + 1; + if (index > themes.length -1) { + index = 0; + } + + location.href=themes[index]; + + // if (location.pathname == current) { + // location.href=opposite; + // } else { + // location.href=current; + // } + +} + +type Buttn struct { + ctx context.Context + text []string + get_url utils_types.FilePath + url utils_types.FilePath + + is_relay bool + extra_style string + attrs templ.Attributes +} +type ButtnOpt func(s *Buttn) + +func WithRelay() ButtnOpt{ + return func(s *Buttn) { + s.is_relay = true + } +} +func WithStyle(style string) ButtnOpt{ + return func(s *Buttn) { + s.extra_style = style + } +} + +func NewButtn( + ctx context.Context, + text []string, + get_url utils_types.FilePath, + url utils_types.FilePath, + opts ...ButtnOpt, + ) *Buttn { + button := &Buttn{ + ctx:ctx, + text: text, + get_url: get_url, + url: url, + } + + for _, opt := range opts { + opt(button) + } + + button.attrs = make(templ.Attributes) + var sb strings.Builder + if button.get_url == urls.Swagger { + sb.WriteString("width:100%; border-radius: 20px; height: 100%; ") + } + + if button.get_url == urls.Thrusters || + button.get_url == urls.Scanners || + button.get_url == urls.Missiles || + button.get_url == urls.Factions || + button.get_url == urls.Missions || + button.get_url == urls.IDRephacks { + sb.WriteString("flex-grow: 2; ") + } + + sb.WriteString(button.extra_style) + button.attrs["style"] =sb.String() + + return button +} + +func (opt *Buttn) HxGet() string { + if opt.is_relay { + return types.GetCtx(opt.ctx).RelayHost + types.GetCtx(opt.ctx).RelayRoot + opt.get_url.ToString() + } + + return types.GetCtx(opt.ctx).SiteRoot + opt.get_url.ToString() +} + +templ Button(opt *Buttn) { + if opt.get_url == urls.Swagger { + + + + + // TODO refactor this hackery + } else { + + } +} + +func AllItemsUrl(url utils_types.FilePath) utils_types.FilePath { + return "all_" + url +} +func NotAllItemsUrl(url utils_types.FilePath) utils_types.FilePath { + return utils_types.FilePath(strings.ReplaceAll(url.ToString(), "all_", "")) +} diff --git a/vendor/github.com/darklab8/fl-darkstat/darkstat/front/tab/pin_td_tr.templ b/vendor/github.com/darklab8/fl-darkstat/darkstat/front/tab/pin_td_tr.templ new file mode 100644 index 0000000..5286665 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/darkstat/front/tab/pin_td_tr.templ @@ -0,0 +1,209 @@ +package tab + +import ( + "github.com/darklab8/fl-darkstat/darkstat/configs_export" + "github.com/darklab8/fl-darkstat/darkstat/front/types" + "github.com/darklab8/fl-darkstat/darkstat/front/frmt" + "context" +) + +type PinMod string + +const ( + MainMode PinMod = "" + PinMode PinMod = "pin" +) + +script Pin(id string) { + if (id.includes("pin")) { + Hide(id) + + if (TableLen(document.querySelector("#table-top-mainpin table")) === 1) { + Hide("table-top-mainpin") + Hide("table-bottom-mainpin") + } + } else { + Unhide("pin"+id) + Unhide("table-top-mainpin") + Unhide("table-bottom-mainpin") + } +} + +script Pin2(id string) { + // htmx adding row to table version for performance + if (id.includes("pin")) { + // deleting htmx added row in table + row = document.querySelector("#" + id) + row.remove() + + if (TableLen(document.querySelector("#table-top-mainpin table")) === 1) { + Hide("table-top-mainpin") + Hide("table-bottom-mainpin") + } + } else { + // htmx adds as another element to table + Unhide("table-top-mainpin") + Unhide("table-bottom-mainpin") + + rows = document.querySelectorAll("#pin"+id) + if (rows.length >= 1) { + for (let i = 1; i < rows.length; i++) { + rows[i].remove() + } + } + } +} + +templ PinTd(nickname string, name string, pinm PinMod) { + + + @frmt.WriteLimit(200) { + { name } + } + + if pinm == MainMode { + pin + } else { + unpin + } + +} + +templ PinTd2(nickname string, name string, pinm PinMod, url string) { + // htmx adding row to table version for performance + + { name } + if pinm==MainMode { + pin + } else { + unpin + } + +} + +templ PinSelectLeftRight(pinm PinMod, url string) { + if pinm == PinMode { + ← + → + } +} + +type InfocardClickOpts struct { + IsRelayHosted bool +} + +templ TdInfoCardClick(infocardKey configs_export.InfocardKey, nickname string, pinm PinMod, shared *types.SharedData, opts InfocardClickOpts) { + @EmbedInfocardForSearch(shared.Infocards, infocardKey) + + +} + +type Tr struct { + nickname string + detailed_url string + pinm PinMod + is_tranport_unreachable bool + from_relay bool +} +type TrOpt func(t *Tr) + +func WithTransportUnreachable(is_tranport_unreachable bool) TrOpt { + return func(t *Tr) { + t.is_tranport_unreachable = is_tranport_unreachable + } +} + +func WithFromRelay() TrOpt { + return func(t *Tr) { + t.from_relay = true + } +} + +func NewTr(nickname string, detailed_url string, pinm PinMod, opts ...TrOpt) *Tr { + t := &Tr{ + nickname: nickname, + detailed_url: detailed_url, + pinm: pinm, + } + + for _, opt := range opts { + opt(t) + } + + return t +} + +templ TrFromMain(tr *Tr) { + @TrFromMainTb2(tr, false) { + { children... } + } +} + +templ TrFromMainTb(nickname string, detailed_url string, pinm PinMod) { + @TrFromMainTb2(NewTr(nickname, detailed_url, pinm), false) { + { children... } + } +} + +func GetSiteRoot(tr *Tr, ctx context.Context) string { + if tr.from_relay { + return types.GetCtx(ctx).RelayHost + types.GetCtx(ctx).RelayRoot + } + + return types.GetCtx(ctx).SiteRoot +} + +templ TrFromMainTb2(tr *Tr, prerender bool) { + + { children... } + +} diff --git a/vendor/github.com/darklab8/fl-darkstat/darkstat/front/tab/ping.templ b/vendor/github.com/darklab8/fl-darkstat/darkstat/front/tab/ping.templ new file mode 100644 index 0000000..e8d60ca --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/darkstat/front/tab/ping.templ @@ -0,0 +1,3 @@ +package tab + +templ Smth() {} \ No newline at end of file diff --git a/vendor/github.com/darklab8/fl-darkstat/darkstat/front/tab/tab.go b/vendor/github.com/darklab8/fl-darkstat/darkstat/front/tab/tab.go new file mode 100644 index 0000000..3aa3666 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/darkstat/front/tab/tab.go @@ -0,0 +1,22 @@ +package tab + +import ( + "strings" + + "github.com/darklab8/fl-darkstat/darkstat/configs_export" +) + +type ShowEmpty bool + +func InfocardURL(infocard_key configs_export.InfocardKey) string { + return "infocards/info_" + strings.ToLower(string(infocard_key)) +} + +func GetFirstLine(infocards configs_export.Infocards, infokey configs_export.InfocardKey) string { + if infocard_lines, ok := infocards[infokey]; ok { + if len(infocard_lines) > 0 { + return string(infocard_lines[0].ToStr()) + } + } + return "" +} diff --git a/vendor/github.com/darklab8/fl-darkstat/darkstat/front/tab/tab.templ b/vendor/github.com/darklab8/fl-darkstat/darkstat/front/tab/tab.templ new file mode 100644 index 0000000..131d294 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/darkstat/front/tab/tab.templ @@ -0,0 +1,133 @@ +package tab + +import ( + +) + +templ TabContent() { +
+ { children... } +
+ +} diff --git a/vendor/github.com/darklab8/fl-darkstat/darkstat/front/tab/tables.templ b/vendor/github.com/darklab8/fl-darkstat/darkstat/front/tab/tables.templ new file mode 100644 index 0000000..5d58358 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/darkstat/front/tab/tables.templ @@ -0,0 +1,108 @@ +package tab +/* +For structured components of darkstat +defining different table components reusable in all tabs +*/ + +templ TableTop() { + +
+ { children... } +
+} +templ TopBottomResizer() { + +
+ +} + +templ TableBottom(){ + @TopBottomResizer() + +
+ { children... } +
+} + +templ LeftRightResizer() { + +
+ +} + +templ LeftTable() { + +
+ { children... } +
+} + +templ InfocardTable() { + @LeftRightResizer() + +
+ { children... } +
+} \ No newline at end of file diff --git a/vendor/github.com/darklab8/fl-darkstat/darkstat/front/types/types.go b/vendor/github.com/darklab8/fl-darkstat/darkstat/front/types/types.go new file mode 100644 index 0000000..0eeae63 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/darkstat/front/types/types.go @@ -0,0 +1,75 @@ +package types + +import ( + "context" + "time" + + "github.com/darklab8/fl-darkstat/configs/cfg" + "github.com/darklab8/fl-darkstat/configs/configs_mapped" + "github.com/darklab8/fl-darkstat/configs/discovery/techcompat" + "github.com/darklab8/fl-darkstat/darkcore/core_types" + "github.com/darklab8/fl-darkstat/darkstat/configs_export" + "github.com/darklab8/fl-data-discovery/autopatcher" + "github.com/darklab8/go-utils/utils/utils_types" +) + +type Theme int64 + +const ( + ThemeNotSet Theme = iota + ThemeLight + ThemeDark + ThemeVanilla +) + +type GlobalParams struct { + Buildpath utils_types.FilePath + Theme Theme + Themes []string + SiteUrl string + SiteRoot string + StaticRoot string + Heading string + Timestamp time.Time + TractorTabName string + + RelayHost string + RelayRoot string +} + +func (g *GlobalParams) GetBuildPath() utils_types.FilePath { + return g.Buildpath +} + +func (g *GlobalParams) GetStaticRoot() string { + return g.StaticRoot +} + +var check core_types.GlobalParamsI = &GlobalParams{} + +func GetCtx(ctx context.Context) *GlobalParams { + return ctx.Value(core_types.GlobalParamsCtxKey).(*GlobalParams) +} + +type FLSRData struct { + ShowFLSR bool +} + +type DiscoveryData struct { + ShowDisco bool + Ids []*configs_export.Tractor + TractorsByID map[cfg.TractorID]*configs_export.Tractor + Config *techcompat.Config + LatestPatch autopatcher.Patch + + Infocards configs_export.Infocards + + OrderedTechcompat configs_export.TechCompatOrderer +} + +type SharedData struct { + DiscoveryData + FLSRData + Mapped *configs_mapped.MappedConfigs + CraftableBaseName string +} diff --git a/vendor/github.com/darklab8/fl-darkstat/darkstat/front/urls/urls.go b/vendor/github.com/darklab8/fl-darkstat/darkstat/front/urls/urls.go new file mode 100644 index 0000000..bd0181e --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/darkstat/front/urls/urls.go @@ -0,0 +1,39 @@ +package urls + +import ( + "github.com/darklab8/go-utils/utils/utils_types" +) + +const ( + Docs utils_types.FilePath = "docs.html" + IndexDocs utils_types.FilePath = "index_docs.html" + + Index utils_types.FilePath = "index.html" + DarkIndex utils_types.FilePath = "dark.html" + VanillaIndex utils_types.FilePath = "vanilla.html" + Bases utils_types.FilePath = "bases.html" + Trades utils_types.FilePath = "trades.html" + Asteroids utils_types.FilePath = "asteroids.html" + TravelRoutes utils_types.FilePath = "travel_routes.html" + Missions utils_types.FilePath = "missions.html" + Factions utils_types.FilePath = "factions.html" + Bribes utils_types.FilePath = "bribes.html" + Commodities utils_types.FilePath = "commodities.html" + Guns utils_types.FilePath = "guns.html" + GunModifiers utils_types.FilePath = "gun_modifiers.html" + Ammo utils_types.FilePath = "ammo.html" + Swagger utils_types.FilePath = "swagger" + Missiles utils_types.FilePath = "missiles.html" + Mines utils_types.FilePath = "mines.html" + Shields utils_types.FilePath = "shields.html" + Thrusters utils_types.FilePath = "thrusters.html" + Ships utils_types.FilePath = "ships.html" + ShipDetails utils_types.FilePath = "ship_details.html" + Tractors utils_types.FilePath = "tractors.html" + IDRephacks utils_types.FilePath = "id_rephacks.html" + Engines utils_types.FilePath = "engines.html" + CounterMeasures utils_types.FilePath = "counter_measures.html" + Scanners utils_types.FilePath = "scanners.html" + PoBs utils_types.FilePath = "pobs.html" + PoBGoods utils_types.FilePath = "pob_goods.html" +) diff --git a/vendor/github.com/darklab8/fl-darkstat/darkstat/settings/logus/logus.go b/vendor/github.com/darklab8/fl-darkstat/darkstat/settings/logus/logus.go new file mode 100644 index 0000000..cbd4749 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/darkstat/settings/logus/logus.go @@ -0,0 +1,10 @@ +package logus + +import ( + _ "github.com/darklab8/fl-darkstat/darkstat/settings" + "github.com/darklab8/go-typelog/typelog" +) + +var Log *typelog.Logger = typelog.NewLogger("darkstat", + typelog.WithLogLevel(typelog.LEVEL_INFO), +) diff --git a/vendor/github.com/darklab8/fl-darkstat/darkstat/settings/settings.go b/vendor/github.com/darklab8/fl-darkstat/darkstat/settings/settings.go new file mode 100644 index 0000000..b032205 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/darkstat/settings/settings.go @@ -0,0 +1,101 @@ +package settings + +import ( + "fmt" + "strings" + + _ "embed" + + "github.com/darklab8/fl-darkstat/configs/configs_mapped" + "github.com/darklab8/fl-darkstat/configs/configs_settings" + darkcore_settings "github.com/darklab8/fl-darkstat/darkcore/settings" + + "github.com/darklab8/go-utils/utils/enverant" + "github.com/darklab8/go-utils/utils/utils_settings" +) + +//go:embed version.txt +var version string + +type DarkstatEnvVars struct { + utils_settings.UtilsEnvs + configs_settings.ConfEnvVars + darkcore_settings.DarkcoreEnvVars + + TractorTabName string + SiteUrl string + SiteRoot string + SiteRootAcceptors string + AppHeading string + AppVersion string + IsDetailed bool + + RelayHost string + RelayRoot string + RelayLoopSecs int + + IsDisabledTradeRouting bool + TradeRoutesDetailedTradeLane bool + + IsCPUProfilerEnabled bool + IsMemProfilerEnabled bool +} + +var Env DarkstatEnvVars + +func init() { + env := enverant.NewEnverant() + Env = DarkstatEnvVars{ + UtilsEnvs: utils_settings.GetEnvs(env), + ConfEnvVars: configs_settings.GetEnvs(env), + DarkcoreEnvVars: darkcore_settings.GetEnvs(env), + TractorTabName: env.GetStr("DARKSTAT_TRACTOR_TAB_NAME", enverant.OrStr("Tractors")), + SiteUrl: env.GetStrOr("SITE_URL", env.GetStr("SITE_HOST", enverant.OrStr(""))), + SiteRoot: env.GetStr("SITE_ROOT", enverant.OrStr("/")), + SiteRootAcceptors: env.GetStr("SITE_ROOT_ACCEPTORS", enverant.OrStr("")), + AppHeading: env.GetStr("FLDARKSTAT_HEADING", enverant.OrStr("")), + AppVersion: getAppVersion(), + IsDetailed: env.GetBoolOr("DARKSTAT_DETAILED", false), + + RelayHost: env.GetStr("RELAY_HOST", enverant.OrStr("")), + RelayRoot: env.GetStr("RELAY_ROOT", enverant.OrStr("/")), + RelayLoopSecs: env.GetIntOr("RELAY_LOOP_SECS", 30), + + TradeRoutesDetailedTradeLane: env.GetBoolOr("DARKSTAT_TRADE_ROUTES_DETAILED_TRADE_LANE", false), + IsDisabledTradeRouting: env.GetBoolOr("CONFIGS_DISABLE_TRADE_ROUTES", false), // BROKEN. DO NOT TURN THIS FEATURE ON. + + IsCPUProfilerEnabled: env.GetBoolOr("IS_CPU_PROFILER_ENABLED", false), + IsMemProfilerEnabled: env.GetBoolOr("IS_MEM_PROFILER_ENABLED", false), + } + + fmt.Sprintln("conf=", Env) +} + +func (e DarkstatEnvVars) GetSiteRootAcceptors() []string { + if e.SiteRootAcceptors == "" { + return []string{} + } + + return strings.Split(e.SiteRootAcceptors, ",") +} + +func IsRelayActive(configs *configs_mapped.MappedConfigs) bool { + if configs.Discovery != nil { + fmt.Println("discovery always wishes to see pobs") + return true + } + + fmt.Println("relay is disabled") + return false +} + +func getAppVersion() string { + // cleaning up version from... debugging logs used during dev env + lines := strings.Split(version, "\n") + for _, line := range lines { + if strings.HasPrefix(line, "v") { + return line + } + } + return version +} diff --git a/vendor/github.com/darklab8/fl-darkstat/darkstat/settings/version.txt b/vendor/github.com/darklab8/fl-darkstat/darkstat/settings/version.txt new file mode 100644 index 0000000..9fac868 --- /dev/null +++ b/vendor/github.com/darklab8/fl-darkstat/darkstat/settings/version.txt @@ -0,0 +1 @@ +v1.64.0 diff --git a/vendor/github.com/darklab8/fl-data-discovery/LICENSE b/vendor/github.com/darklab8/fl-data-discovery/LICENSE new file mode 100644 index 0000000..64a32d8 --- /dev/null +++ b/vendor/github.com/darklab8/fl-data-discovery/LICENSE @@ -0,0 +1,661 @@ +GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + +Copyright (C) 2007 Free Software Foundation, Inc. +Everyone is permitted to copy and distribute verbatim copies +of this license document, but changing it is not allowed. + + Preamble + +The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + +The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. + +When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + +Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + +A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + +The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + +An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + +The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + +0. Definitions. + +"This License" refers to version 3 of the GNU Affero General Public License. + +"Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + +"The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + +To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + +A "covered work" means either the unmodified Program or a work based +on the Program. + +To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + +To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + +An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + +1. Source Code. + +The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + +A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + +The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + +The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + +The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + +The Corresponding Source for a work in source code form is that +same work. + +2. Basic Permissions. + +All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + +You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + +Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + +3. Protecting Users' Legal Rights From Anti-Circumvention Law. + +No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + +When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + +4. Conveying Verbatim Copies. + +You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + +You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + +5. Conveying Modified Source Versions. + +You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + +a) The work must carry prominent notices stating that you modified +it, and giving a relevant date. + +b) The work must carry prominent notices stating that it is +released under this License and any conditions added under section +7. This requirement modifies the requirement in section 4 to +"keep intact all notices". + +c) You must license the entire work, as a whole, under this +License to anyone who comes into possession of a copy. This +License will therefore apply, along with any applicable section 7 +additional terms, to the whole of the work, and all its parts, +regardless of how they are packaged. This License gives no +permission to license the work in any other way, but it does not +invalidate such permission if you have separately received it. + +d) If the work has interactive user interfaces, each must display +Appropriate Legal Notices; however, if the Program has interactive +interfaces that do not display Appropriate Legal Notices, your +work need not make them do so. + +A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + +6. Conveying Non-Source Forms. + +You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + +a) Convey the object code in, or embodied in, a physical product +(including a physical distribution medium), accompanied by the +Corresponding Source fixed on a durable physical medium +customarily used for software interchange. + +b) Convey the object code in, or embodied in, a physical product +(including a physical distribution medium), accompanied by a +written offer, valid for at least three years and valid for as +long as you offer spare parts or customer support for that product +model, to give anyone who possesses the object code either (1) a +copy of the Corresponding Source for all the software in the +product that is covered by this License, on a durable physical +medium customarily used for software interchange, for a price no +more than your reasonable cost of physically performing this +conveying of source, or (2) access to copy the +Corresponding Source from a network server at no charge. + +c) Convey individual copies of the object code with a copy of the +written offer to provide the Corresponding Source. This +alternative is allowed only occasionally and noncommercially, and +only if you received the object code with such an offer, in accord +with subsection 6b. + +d) Convey the object code by offering access from a designated +place (gratis or for a charge), and offer equivalent access to the +Corresponding Source in the same way through the same place at no +further charge. You need not require recipients to copy the +Corresponding Source along with the object code. If the place to +copy the object code is a network server, the Corresponding Source +may be on a different server (operated by you or a third party) +that supports equivalent copying facilities, provided you maintain +clear directions next to the object code saying where to find the +Corresponding Source. Regardless of what server hosts the +Corresponding Source, you remain obligated to ensure that it is +available for as long as needed to satisfy these requirements. + +e) Convey the object code using peer-to-peer transmission, provided +you inform other peers where the object code and Corresponding +Source of the work are being offered to the general public at no +charge under subsection 6d. + +A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + +A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + +"Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + +If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + +The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + +Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + +7. Additional Terms. + +"Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + +When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + +Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + +a) Disclaiming warranty or limiting liability differently from the +terms of sections 15 and 16 of this License; or + +b) Requiring preservation of specified reasonable legal notices or +author attributions in that material or in the Appropriate Legal +Notices displayed by works containing it; or + +c) Prohibiting misrepresentation of the origin of that material, or +requiring that modified versions of such material be marked in +reasonable ways as different from the original version; or + +d) Limiting the use for publicity purposes of names of licensors or +authors of the material; or + +e) Declining to grant rights under trademark law for use of some +trade names, trademarks, or service marks; or + +f) Requiring indemnification of licensors and authors of that +material by anyone who conveys the material (or modified versions of +it) with contractual assumptions of liability to the recipient, for +any liability that these contractual assumptions directly impose on +those licensors and authors. + +All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + +If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + +Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + +8. Termination. + +You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + +However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + +Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + +Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + +9. Acceptance Not Required for Having Copies. + +You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + +10. Automatic Licensing of Downstream Recipients. + +Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + +An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + +You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + +11. Patents. + +A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + +A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + +Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + +In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + +If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + +If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + +A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + +Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + +12. No Surrender of Others' Freedom. + +If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + +13. Remote Network Interaction; Use with the GNU General Public License. + +Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + +Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the work with which it is combined will remain governed by version +3 of the GNU General Public License. + +14. Revised Versions of this License. + +The Free Software Foundation may publish revised and/or new versions of +the GNU Affero General Public License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU Affero General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU Affero General Public License, you may choose any version ever published +by the Free Software Foundation. + +If the Program specifies that a proxy can decide which future +versions of the GNU Affero General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + +Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + +15. Disclaimer of Warranty. + +THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +16. Limitation of Liability. + +IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + +17. Interpretation of Sections 15 and 16. + +If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + +How to Apply These Terms to Your New Programs + +If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + +To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + +fl-data-discovery deployment of fl-darkstat for Freelancer Discovery +Copyright (C) 2024 Andrei Novoselov https://github.com/darklab8/fl-data-discovery + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published +by the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + +If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for the +specific requirements. + +You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU AGPL, see +. \ No newline at end of file diff --git a/vendor/github.com/darklab8/fl-data-discovery/autopatcher/autopatcher.go b/vendor/github.com/darklab8/fl-data-discovery/autopatcher/autopatcher.go new file mode 100644 index 0000000..56f9574 --- /dev/null +++ b/vendor/github.com/darklab8/fl-data-discovery/autopatcher/autopatcher.go @@ -0,0 +1,451 @@ +package autopatcher + +import ( + "archive/zip" + "crypto/md5" + "encoding/hex" + "encoding/json" + "encoding/xml" + "fmt" + "io" + "io/fs" + "net/http" + "os" + "path/filepath" + "runtime" + "strings" +) + +type RequestResp struct { + Body []byte + StatusCode int +} + +func Request(url string) RequestResp { + req, err := http.NewRequest(http.MethodGet, url, nil) + if err != nil { + fmt.Printf("client: could not create request: %s\n", err) + os.Exit(1) + } + + res, err := http.DefaultClient.Do(req) + if err != nil { + fmt.Printf("client: error making http request: %s\n", err) + os.Exit(1) + } + + fmt.Printf("client: got response!\n") + fmt.Printf("client: status code: %d\n", res.StatusCode) + + resBody, err := io.ReadAll(res.Body) + if err != nil { + fmt.Printf("client: could not read response body: %s\n", err) + os.Exit(1) + } + // fmt.Printf("client: response body: %s\n", resBody) + return RequestResp{ + Body: resBody, + StatusCode: res.StatusCode, + } +} + +func downloadFile(filepath string, url string) (err error) { + + // Get the data + resp, err := http.Get(url) + if err != nil { + return err + } + defer resp.Body.Close() + + // Check server response + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("bad status: %s", resp.Status) + } + + // Create the file + out, err := os.Create(filepath) + if err != nil { + return err + } + defer out.Close() + + // Writer the body to file + _, err = io.Copy(out, resp.Body) + if err != nil { + return err + } + + return nil +} + +func fileExists(fpath string) bool { + if _, err := os.Stat(fpath); err == nil { + // path/to/whatever exists + return true + } + return false +} + +/* +Unzip is copy paste from +https://stackoverflow.com/questions/20357223/easy-way-to-unzip-file +https://stackoverflow.com/a/24792688 +*/ +func Unzip(src, dest string) error { + r, err := zip.OpenReader(src) + if err != nil { + return err + } + defer func() { + if err := r.Close(); err != nil { + panic(err) + } + }() + + os.MkdirAll(dest, 0777) + + // Closure to address file descriptors issue with all the deferred .Close() methods + extractAndWriteFile := func(f *zip.File) error { + rc, err := f.Open() + if err != nil { + return err + } + defer func() { + if err := rc.Close(); err != nil { + panic(err) + } + }() + + path := filepath.Join(dest, f.Name) + + // Check for ZipSlip (Directory traversal) + if !strings.HasPrefix(path, filepath.Clean(dest)+string(os.PathSeparator)) { + return fmt.Errorf("illegal file path: %s", path) + } + + if f.FileInfo().IsDir() { + os.MkdirAll(path, 0777) + } else { + os.MkdirAll(filepath.Dir(path), 0777) + f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0777) + if err != nil { + return err + } + defer func() { + if err := f.Close(); err != nil { + panic(err) + } + }() + + _, err = io.Copy(f, rc) + if err != nil { + return err + } + } + return nil + } + + for _, f := range r.File { + err := extractAndWriteFile(f) + if err != nil { + return err + } + } + + return nil +} + +type PatcherData struct { + XMLName xml.Name `xml:"PatcherData"` + Text string `xml:",chardata"` + PatchList struct { + Text string `xml:",chardata"` + Patch []struct { + Text string `xml:",chardata"` + URL string `xml:"url,attr"` + Md5hash string `xml:"md5hash,attr"` + } `xml:"patch"` + } `xml:"PatchList"` +} + +type Patch struct { + Filename string + Url string + Hash PatchHash + Name string +} + +func (patch Patch) GetFilepath() string { + return filepath.Join("patches", patch.Filename) +} + +func (patch Patch) GetFolderPath() string { + return filepath.Join("patches", strings.ReplaceAll(patch.Filename, ".zip", "")) +} + +func parseForPatches(discovery_url string, body []byte) []Patch { + var patches []Patch + var Page PatcherData + xml.Unmarshal(body, &Page) + + for _, patch := range Page.PatchList.Patch { + // fmt.Println(patch) + patches = append(patches, Patch{ + Filename: patch.URL, + Url: discovery_url + patch.URL, + Hash: PatchHash(patch.Md5hash), + Name: patch.Text, + }) + } + return patches +} + +func downloadPatch(patch Patch) { + os.MkdirAll("patches", 0777) + if fileExists(patch.GetFilepath()) { + fmt.Println("fpath already eixsts, fpath=", patch.GetFilepath()) + return + } + + err := downloadFile(patch.GetFilepath(), patch.Url) + if err != nil { + fmt.Println("not able to download url", err, patch, "url=", patch.Url) + os.Exit(1) + } + fmt.Println("downloaded file", patch.GetFilepath()) +} + +type File struct { + filepath_ string +} + +func (f File) GetPath() string { + return f.filepath_ +} + +func (f File) GetRelPathTo(root string) string { + path := strings.ReplaceAll(f.filepath_, root+PATH_SEPARATOR, "") + return path +} + +func (f File) GetLowerPath() string { + return strings.ToLower(f.filepath_) +} + +func NewFile(path string) File { + f := File{filepath_: path} + return f +} + +type Filesystem struct { + Files []File + LowerMapFiles map[string]File + + Folders []File + LowerMapFolders map[string]File +} + +func ScanCaseInsensitiveFS(fs_path string) Filesystem { + myfs := Filesystem{ + LowerMapFiles: make(map[string]File), + LowerMapFolders: make(map[string]File), + } + filepath.WalkDir(fs_path, func(path string, d fs.DirEntry, err error) error { + if err != nil { + return err + } + if !d.IsDir() { + file := NewFile(path) + myfs.Files = append(myfs.Files, file) + myfs.LowerMapFiles[file.GetLowerPath()] = file + } else { + folder := NewFile(path) + myfs.Folders = append(myfs.Folders, folder) + myfs.LowerMapFolders[folder.GetLowerPath()] = folder + } + return nil + + }) + return myfs +} + +func WriteToFile(path string, content []byte) { + destination, err := os.Create(path) + if err != nil { + panic(fmt.Sprintln("os.Create:", err)) + } + defer destination.Close() + + _, err = destination.Write(content) + if err != nil { + panic(fmt.Sprintln("failed to write file", err)) + } +} + +type BadassRoot struct { + XMLName xml.Name `xml:"BadassRoot"` + Text string `xml:",chardata"` + Xsd string `xml:"xsd,attr"` + Xsi string `xml:"xsi,attr"` + PatchHistory struct { + Text string `xml:",chardata"` + Patch []string `xml:"Patch"` + } `xml:"PatchHistory"` +} + +type PatchHash string + +func readLauncherConfig() (map[PatchHash]string, []string) { + body, err := os.ReadFile("launcherconfig.xml") + if err != nil { + panic(fmt.Sprintln("failed to read launcherconfig", err)) + } + + var patches map[PatchHash]string = make(map[PatchHash]string) + var Page BadassRoot + xml.Unmarshal(body, &Page) + + for _, patch := range Page.PatchHistory.Patch { + patches[PatchHash(patch)] = "" + } + + str := string(body) + file_lines := strings.Split(str, "\n") + return patches, file_lines +} + +/* +AdjustFoldersInPatch replaces folder names in path to relevant case sensitive folders +And creates them if they don't exist +*/ +func AdjustFoldersInPath(relative_patch_filepath string, freelancer_folder Filesystem) string { + patch_chain_folders := strings.Split(relative_patch_filepath, PATH_SEPARATOR) + patch_chain_folders = patch_chain_folders[:len(patch_chain_folders)-1] // minus filename + + for i := 1; i <= len(patch_chain_folders); i++ { + folders_chain := patch_chain_folders[:i] + patch_target_folder := filepath.Join(folders_chain...) + + if folder, found := freelancer_folder.LowerMapFolders[strings.ToLower(patch_target_folder)]; found { + relative_patch_filepath = strings.ReplaceAll(relative_patch_filepath, patch_target_folder, folder.GetPath()) + + // refreshing + patch_chain_folders = strings.Split(relative_patch_filepath, PATH_SEPARATOR) + patch_chain_folders = patch_chain_folders[:len(patch_chain_folders)-1] // minus filename + } else { + err := os.MkdirAll(patch_target_folder, os.ModePerm) + if err != nil { + panic(fmt.Sprintln("failed creating mkdirall", err)) + } + } + } + + return relative_patch_filepath +} + +var PATH_SEPARATOR = "" + +func init() { + if runtime.GOOS == "windows" { + PATH_SEPARATOR = "\\" + } else { + PATH_SEPARATOR = "/" + } +} + +func RunAutopatcher() { + discovery_url := "https://patch.discoverygc.com/" + discovery_path_url := discovery_url + "patchlist.xml" + resp := Request(discovery_path_url) + + patches := parseForPatches(discovery_url, resp.Body) + + patchhistory, file_lines := readLauncherConfig() + + var applied_patches []Patch + + for _, patch := range patches { + + if patch.Hash == "E6F377FC78A4833128EA685C29D47458" { + fmt.Println() + } + + if _, found := patchhistory[patch.Hash]; found { + fmt.Println("patch is already installed", patch) + continue + } + + downloadPatch(patch) + + patch_body, _ := os.ReadFile(patch.GetFilepath()) + hash := md5.Sum(patch_body) + md5_result := hex.EncodeToString(hash[:]) + fmt.Println("md5_result=", md5_result) + + if md5_result != strings.ToLower(string(patch.Hash)) { + panic(fmt.Sprintln("md5 hash sum is not matching", "expected=", patch.Hash, " but bound=", md5_result)) + } + + // md5, err := checksum.MD5sum(patch.GetFilepath()) + // fmt.Println(md5) + // fmt.Println("md5_result=", md5, err) + + Unzip(patch.GetFilepath(), patch.GetFolderPath()) + + freelancer_folder := ScanCaseInsensitiveFS(".") + patch_folder := ScanCaseInsensitiveFS(patch.GetFolderPath()) + + for _, file := range patch_folder.Files { + + content, err := os.ReadFile(file.GetPath()) + if err != nil { + panic(fmt.Sprintln("failed to read file", err)) + } + + relative_patch_filepath := file.GetRelPathTo(patch.GetFolderPath()) + + if strings.Contains(relative_patch_filepath, ".gitignore") { + continue + } + + if freelancer_path, file_exists := freelancer_folder.LowerMapFiles[strings.ToLower(relative_patch_filepath)]; file_exists { + os.Remove(freelancer_path.GetPath()) + } + + relative_patch_filepath = AdjustFoldersInPath(relative_patch_filepath, freelancer_folder) + + WriteToFile(relative_patch_filepath, content) + + } + + os.RemoveAll(patch.GetFolderPath()) + + fmt.Println("applied patch", patch) + patch_marshaled, _ := json.Marshal(patch) + os.WriteFile(AutopatherFilename, patch_marshaled, 0666) + + applied_patches = append(applied_patches, patch) + } + + var patch_file_start []string + var patch_file_end []string + for line_index, _ := range file_lines { + if strings.Contains(file_lines[line_index], "") && !strings.Contains(file_lines[line_index+1], "") { + patch_file_start = file_lines[:line_index+1] + patch_file_end = file_lines[line_index+1:] + } + } + if len(patch_file_start) == 0 { + panic("not found patch line index, where to insert") + } + var new_patch_file_lines []string + new_patch_file_lines = append(new_patch_file_lines, patch_file_start...) + for _, patch := range applied_patches { + new_patch_file_lines = append(new_patch_file_lines, fmt.Sprintf(" %s\r", patch.Hash)) + } + new_patch_file_lines = append(new_patch_file_lines, patch_file_end...) + os.WriteFile("launcherconfig.xml", []byte(strings.Join(new_patch_file_lines, "\n")), 0666) +} + +const AutopatherFilename = "autopatcher.latest_patch.json" diff --git a/vendor/github.com/darklab8/go-utils/utils/utils_filepath/filepath.go b/vendor/github.com/darklab8/go-utils/utils/utils_filepath/filepath.go new file mode 100644 index 0000000..1a1590a --- /dev/null +++ b/vendor/github.com/darklab8/go-utils/utils/utils_filepath/filepath.go @@ -0,0 +1,25 @@ +package utils_filepath + +import ( + "path/filepath" + "strings" + + "github.com/darklab8/go-utils/utils" + "github.com/darklab8/go-utils/utils/utils_types" +) + +func Join(paths ...utils_types.FilePath) utils_types.FilePath { + return utils_types.FilePath(filepath.Join(utils.CompL(paths, func(path utils_types.FilePath) string { return string(path) })...)) +} + +func Dir(path utils_types.FilePath) utils_types.FilePath { + return utils_types.FilePath(filepath.Dir(string(path))) +} + +func Base(path utils_types.FilePath) utils_types.FilePath { + return utils_types.FilePath(filepath.Base(string(path))) +} + +func Contains(path utils_types.FilePath, subpath utils_types.FilePath) bool { + return strings.Contains(path.ToString(), subpath.ToString()) +} diff --git a/vendor/github.com/darklab8/go-utils/utils/utils_types/types.go b/vendor/github.com/darklab8/go-utils/utils/utils_types/types.go index 850c930..1832b1f 100644 --- a/vendor/github.com/darklab8/go-utils/utils/utils_types/types.go +++ b/vendor/github.com/darklab8/go-utils/utils/utils_types/types.go @@ -1,8 +1,11 @@ package utils_types import ( + "crypto/md5" "embed" "fmt" + "io" + "io/fs" "log" "path/filepath" "strings" @@ -40,7 +43,7 @@ type GetFilesParams struct { AllowedExtensions []string } -func GetFiles(fs embed.FS, params GetFilesParams) []File { +func GetFiles(filesystem embed.FS, params GetFilesParams) []File { if len(params.AllowedExtensions) == 0 { params.AllowedExtensions = []string{"js", "css", "png", "jpeg"} } @@ -48,7 +51,7 @@ func GetFiles(fs embed.FS, params GetFilesParams) []File { params.RootFolder = "." } - files, err := fs.ReadDir(params.RootFolder.ToString()) + files, err := filesystem.ReadDir(params.RootFolder.ToString()) var result []File if err != nil { log.Fatal(err) @@ -61,7 +64,7 @@ func GetFiles(fs embed.FS, params GetFilesParams) []File { params.relFolder = params.relFolder.Join(f.Name()) params.RootFolder = params.RootFolder.Join(f.Name()) result = append(result, - GetFiles(fs, params)..., + GetFiles(filesystem, params)..., ) } else { splitted := strings.Split(f.Name(), ".") @@ -71,9 +74,14 @@ func GetFiles(fs embed.FS, params GetFilesParams) []File { } path := params.RootFolder.Join(f.Name()) - content, err := fs.ReadFile(path.ToString()) + requested := strings.ReplaceAll(path.ToString(), "\\", "/") // fix for windows + content, err := filesystem.ReadFile(requested) if err != nil { - panic(fmt.Sprintln("failed to read file from embeded fs of ", path)) + PrintFilesForDebug(filesystem) + fmt.Println(err.Error(), "failed to read file from embeded fs of", + "path=", path, + "requested", requested, + ) } is_allowed_extension := false @@ -98,3 +106,25 @@ func GetFiles(fs embed.FS, params GetFilesParams) []File { } return result } + +func PrintFilesForDebug(filesystem embed.FS) { + fs.WalkDir(filesystem, ".", func(p string, d fs.DirEntry, err error) error { + if !d.IsDir() { + st, _ := fs.Stat(filesystem, p) + r, _ := filesystem.Open(p) + defer r.Close() + + // Read prefix + var buf [md5.Size]byte + n, _ := io.ReadFull(r, buf[:]) + + // Hash remainder + h := md5.New() + _, _ = io.Copy(h, r) + s := h.Sum(nil) + + fmt.Printf("%s %d %x %x\n", p, st.Size(), buf[:n], s) + } + return nil + }) +} diff --git a/vendor/github.com/stretchr/testify/assert/assertion_compare.go b/vendor/github.com/stretchr/testify/assert/assertion_compare.go index 4d4b4aa..7e19eba 100644 --- a/vendor/github.com/stretchr/testify/assert/assertion_compare.go +++ b/vendor/github.com/stretchr/testify/assert/assertion_compare.go @@ -7,10 +7,13 @@ import ( "time" ) -type CompareType int +// Deprecated: CompareType has only ever been for internal use and has accidentally been published since v1.6.0. Do not use it. +type CompareType = compareResult + +type compareResult int const ( - compareLess CompareType = iota - 1 + compareLess compareResult = iota - 1 compareEqual compareGreater ) @@ -39,7 +42,7 @@ var ( bytesType = reflect.TypeOf([]byte{}) ) -func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) { +func compare(obj1, obj2 interface{}, kind reflect.Kind) (compareResult, bool) { obj1Value := reflect.ValueOf(obj1) obj2Value := reflect.ValueOf(obj2) @@ -325,7 +328,13 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) { timeObj2 = obj2Value.Convert(timeType).Interface().(time.Time) } - return compare(timeObj1.UnixNano(), timeObj2.UnixNano(), reflect.Int64) + if timeObj1.Before(timeObj2) { + return compareLess, true + } + if timeObj1.Equal(timeObj2) { + return compareEqual, true + } + return compareGreater, true } case reflect.Slice: { @@ -345,7 +354,7 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) { bytesObj2 = obj2Value.Convert(bytesType).Interface().([]byte) } - return CompareType(bytes.Compare(bytesObj1, bytesObj2)), true + return compareResult(bytes.Compare(bytesObj1, bytesObj2)), true } case reflect.Uintptr: { @@ -381,7 +390,7 @@ func Greater(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface if h, ok := t.(tHelper); ok { h.Helper() } - return compareTwoValues(t, e1, e2, []CompareType{compareGreater}, "\"%v\" is not greater than \"%v\"", msgAndArgs...) + return compareTwoValues(t, e1, e2, []compareResult{compareGreater}, "\"%v\" is not greater than \"%v\"", msgAndArgs...) } // GreaterOrEqual asserts that the first element is greater than or equal to the second @@ -394,7 +403,7 @@ func GreaterOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...in if h, ok := t.(tHelper); ok { h.Helper() } - return compareTwoValues(t, e1, e2, []CompareType{compareGreater, compareEqual}, "\"%v\" is not greater than or equal to \"%v\"", msgAndArgs...) + return compareTwoValues(t, e1, e2, []compareResult{compareGreater, compareEqual}, "\"%v\" is not greater than or equal to \"%v\"", msgAndArgs...) } // Less asserts that the first element is less than the second @@ -406,7 +415,7 @@ func Less(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) if h, ok := t.(tHelper); ok { h.Helper() } - return compareTwoValues(t, e1, e2, []CompareType{compareLess}, "\"%v\" is not less than \"%v\"", msgAndArgs...) + return compareTwoValues(t, e1, e2, []compareResult{compareLess}, "\"%v\" is not less than \"%v\"", msgAndArgs...) } // LessOrEqual asserts that the first element is less than or equal to the second @@ -419,7 +428,7 @@ func LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...inter if h, ok := t.(tHelper); ok { h.Helper() } - return compareTwoValues(t, e1, e2, []CompareType{compareLess, compareEqual}, "\"%v\" is not less than or equal to \"%v\"", msgAndArgs...) + return compareTwoValues(t, e1, e2, []compareResult{compareLess, compareEqual}, "\"%v\" is not less than or equal to \"%v\"", msgAndArgs...) } // Positive asserts that the specified element is positive @@ -431,7 +440,7 @@ func Positive(t TestingT, e interface{}, msgAndArgs ...interface{}) bool { h.Helper() } zero := reflect.Zero(reflect.TypeOf(e)) - return compareTwoValues(t, e, zero.Interface(), []CompareType{compareGreater}, "\"%v\" is not positive", msgAndArgs...) + return compareTwoValues(t, e, zero.Interface(), []compareResult{compareGreater}, "\"%v\" is not positive", msgAndArgs...) } // Negative asserts that the specified element is negative @@ -443,10 +452,10 @@ func Negative(t TestingT, e interface{}, msgAndArgs ...interface{}) bool { h.Helper() } zero := reflect.Zero(reflect.TypeOf(e)) - return compareTwoValues(t, e, zero.Interface(), []CompareType{compareLess}, "\"%v\" is not negative", msgAndArgs...) + return compareTwoValues(t, e, zero.Interface(), []compareResult{compareLess}, "\"%v\" is not negative", msgAndArgs...) } -func compareTwoValues(t TestingT, e1 interface{}, e2 interface{}, allowedComparesResults []CompareType, failMessage string, msgAndArgs ...interface{}) bool { +func compareTwoValues(t TestingT, e1 interface{}, e2 interface{}, allowedComparesResults []compareResult, failMessage string, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } @@ -469,7 +478,7 @@ func compareTwoValues(t TestingT, e1 interface{}, e2 interface{}, allowedCompare return true } -func containsValue(values []CompareType, value CompareType) bool { +func containsValue(values []compareResult, value compareResult) bool { for _, v := range values { if v == value { return true diff --git a/vendor/github.com/stretchr/testify/assert/assertion_format.go b/vendor/github.com/stretchr/testify/assert/assertion_format.go index 3ddab10..1906341 100644 --- a/vendor/github.com/stretchr/testify/assert/assertion_format.go +++ b/vendor/github.com/stretchr/testify/assert/assertion_format.go @@ -104,8 +104,8 @@ func EqualExportedValuesf(t TestingT, expected interface{}, actual interface{}, return EqualExportedValues(t, expected, actual, append([]interface{}{msg}, args...)...) } -// EqualValuesf asserts that two objects are equal or convertible to the same types -// and equal. +// EqualValuesf asserts that two objects are equal or convertible to the larger +// type and equal. // // assert.EqualValuesf(t, uint32(123), int32(123), "error message %s", "formatted") func EqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool { @@ -186,7 +186,7 @@ func Eventuallyf(t TestingT, condition func() bool, waitFor time.Duration, tick // assert.EventuallyWithTf(t, func(c *assert.CollectT, "error message %s", "formatted") { // // add assertions as needed; any assertion failure will fail the current tick // assert.True(c, externalValue, "expected 'externalValue' to be true") -// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false") +// }, 10*time.Second, 1*time.Second, "external state has not changed to 'true'; still false") func EventuallyWithTf(t TestingT, condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -568,6 +568,23 @@ func NotContainsf(t TestingT, s interface{}, contains interface{}, msg string, a return NotContains(t, s, contains, append([]interface{}{msg}, args...)...) } +// NotElementsMatchf asserts that the specified listA(array, slice...) is NOT equal to specified +// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements, +// the number of appearances of each of them in both lists should not match. +// This is an inverse of ElementsMatch. +// +// assert.NotElementsMatchf(t, [1, 1, 2, 3], [1, 1, 2, 3], "error message %s", "formatted") -> false +// +// assert.NotElementsMatchf(t, [1, 1, 2, 3], [1, 2, 3], "error message %s", "formatted") -> true +// +// assert.NotElementsMatchf(t, [1, 2, 3], [1, 2, 4], "error message %s", "formatted") -> true +func NotElementsMatchf(t TestingT, listA interface{}, listB interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return NotElementsMatch(t, listA, listB, append([]interface{}{msg}, args...)...) +} + // NotEmptyf asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either // a slice or a channel with len == 0. // @@ -604,7 +621,16 @@ func NotEqualValuesf(t TestingT, expected interface{}, actual interface{}, msg s return NotEqualValues(t, expected, actual, append([]interface{}{msg}, args...)...) } -// NotErrorIsf asserts that at none of the errors in err's chain matches target. +// NotErrorAsf asserts that none of the errors in err's chain matches target, +// but if so, sets target to that error value. +func NotErrorAsf(t TestingT, err error, target interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return NotErrorAs(t, err, target, append([]interface{}{msg}, args...)...) +} + +// NotErrorIsf asserts that none of the errors in err's chain matches target. // This is a wrapper for errors.Is. func NotErrorIsf(t TestingT, err error, target error, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { diff --git a/vendor/github.com/stretchr/testify/assert/assertion_forward.go b/vendor/github.com/stretchr/testify/assert/assertion_forward.go index a84e09b..2162908 100644 --- a/vendor/github.com/stretchr/testify/assert/assertion_forward.go +++ b/vendor/github.com/stretchr/testify/assert/assertion_forward.go @@ -186,8 +186,8 @@ func (a *Assertions) EqualExportedValuesf(expected interface{}, actual interface return EqualExportedValuesf(a.t, expected, actual, msg, args...) } -// EqualValues asserts that two objects are equal or convertible to the same types -// and equal. +// EqualValues asserts that two objects are equal or convertible to the larger +// type and equal. // // a.EqualValues(uint32(123), int32(123)) func (a *Assertions) EqualValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { @@ -197,8 +197,8 @@ func (a *Assertions) EqualValues(expected interface{}, actual interface{}, msgAn return EqualValues(a.t, expected, actual, msgAndArgs...) } -// EqualValuesf asserts that two objects are equal or convertible to the same types -// and equal. +// EqualValuesf asserts that two objects are equal or convertible to the larger +// type and equal. // // a.EqualValuesf(uint32(123), int32(123), "error message %s", "formatted") func (a *Assertions) EqualValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool { @@ -336,7 +336,7 @@ func (a *Assertions) Eventually(condition func() bool, waitFor time.Duration, ti // a.EventuallyWithT(func(c *assert.CollectT) { // // add assertions as needed; any assertion failure will fail the current tick // assert.True(c, externalValue, "expected 'externalValue' to be true") -// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false") +// }, 10*time.Second, 1*time.Second, "external state has not changed to 'true'; still false") func (a *Assertions) EventuallyWithT(condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -361,7 +361,7 @@ func (a *Assertions) EventuallyWithT(condition func(collect *CollectT), waitFor // a.EventuallyWithTf(func(c *assert.CollectT, "error message %s", "formatted") { // // add assertions as needed; any assertion failure will fail the current tick // assert.True(c, externalValue, "expected 'externalValue' to be true") -// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false") +// }, 10*time.Second, 1*time.Second, "external state has not changed to 'true'; still false") func (a *Assertions) EventuallyWithTf(condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1128,6 +1128,40 @@ func (a *Assertions) NotContainsf(s interface{}, contains interface{}, msg strin return NotContainsf(a.t, s, contains, msg, args...) } +// NotElementsMatch asserts that the specified listA(array, slice...) is NOT equal to specified +// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements, +// the number of appearances of each of them in both lists should not match. +// This is an inverse of ElementsMatch. +// +// a.NotElementsMatch([1, 1, 2, 3], [1, 1, 2, 3]) -> false +// +// a.NotElementsMatch([1, 1, 2, 3], [1, 2, 3]) -> true +// +// a.NotElementsMatch([1, 2, 3], [1, 2, 4]) -> true +func (a *Assertions) NotElementsMatch(listA interface{}, listB interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return NotElementsMatch(a.t, listA, listB, msgAndArgs...) +} + +// NotElementsMatchf asserts that the specified listA(array, slice...) is NOT equal to specified +// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements, +// the number of appearances of each of them in both lists should not match. +// This is an inverse of ElementsMatch. +// +// a.NotElementsMatchf([1, 1, 2, 3], [1, 1, 2, 3], "error message %s", "formatted") -> false +// +// a.NotElementsMatchf([1, 1, 2, 3], [1, 2, 3], "error message %s", "formatted") -> true +// +// a.NotElementsMatchf([1, 2, 3], [1, 2, 4], "error message %s", "formatted") -> true +func (a *Assertions) NotElementsMatchf(listA interface{}, listB interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return NotElementsMatchf(a.t, listA, listB, msg, args...) +} + // NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either // a slice or a channel with len == 0. // @@ -1200,7 +1234,25 @@ func (a *Assertions) NotEqualf(expected interface{}, actual interface{}, msg str return NotEqualf(a.t, expected, actual, msg, args...) } -// NotErrorIs asserts that at none of the errors in err's chain matches target. +// NotErrorAs asserts that none of the errors in err's chain matches target, +// but if so, sets target to that error value. +func (a *Assertions) NotErrorAs(err error, target interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return NotErrorAs(a.t, err, target, msgAndArgs...) +} + +// NotErrorAsf asserts that none of the errors in err's chain matches target, +// but if so, sets target to that error value. +func (a *Assertions) NotErrorAsf(err error, target interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return NotErrorAsf(a.t, err, target, msg, args...) +} + +// NotErrorIs asserts that none of the errors in err's chain matches target. // This is a wrapper for errors.Is. func (a *Assertions) NotErrorIs(err error, target error, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { @@ -1209,7 +1261,7 @@ func (a *Assertions) NotErrorIs(err error, target error, msgAndArgs ...interface return NotErrorIs(a.t, err, target, msgAndArgs...) } -// NotErrorIsf asserts that at none of the errors in err's chain matches target. +// NotErrorIsf asserts that none of the errors in err's chain matches target. // This is a wrapper for errors.Is. func (a *Assertions) NotErrorIsf(err error, target error, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { diff --git a/vendor/github.com/stretchr/testify/assert/assertion_order.go b/vendor/github.com/stretchr/testify/assert/assertion_order.go index 00df62a..1d2f718 100644 --- a/vendor/github.com/stretchr/testify/assert/assertion_order.go +++ b/vendor/github.com/stretchr/testify/assert/assertion_order.go @@ -6,7 +6,7 @@ import ( ) // isOrdered checks that collection contains orderable elements. -func isOrdered(t TestingT, object interface{}, allowedComparesResults []CompareType, failMessage string, msgAndArgs ...interface{}) bool { +func isOrdered(t TestingT, object interface{}, allowedComparesResults []compareResult, failMessage string, msgAndArgs ...interface{}) bool { objKind := reflect.TypeOf(object).Kind() if objKind != reflect.Slice && objKind != reflect.Array { return false @@ -50,7 +50,7 @@ func isOrdered(t TestingT, object interface{}, allowedComparesResults []CompareT // assert.IsIncreasing(t, []float{1, 2}) // assert.IsIncreasing(t, []string{"a", "b"}) func IsIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { - return isOrdered(t, object, []CompareType{compareLess}, "\"%v\" is not less than \"%v\"", msgAndArgs...) + return isOrdered(t, object, []compareResult{compareLess}, "\"%v\" is not less than \"%v\"", msgAndArgs...) } // IsNonIncreasing asserts that the collection is not increasing @@ -59,7 +59,7 @@ func IsIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) boo // assert.IsNonIncreasing(t, []float{2, 1}) // assert.IsNonIncreasing(t, []string{"b", "a"}) func IsNonIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { - return isOrdered(t, object, []CompareType{compareEqual, compareGreater}, "\"%v\" is not greater than or equal to \"%v\"", msgAndArgs...) + return isOrdered(t, object, []compareResult{compareEqual, compareGreater}, "\"%v\" is not greater than or equal to \"%v\"", msgAndArgs...) } // IsDecreasing asserts that the collection is decreasing @@ -68,7 +68,7 @@ func IsNonIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) // assert.IsDecreasing(t, []float{2, 1}) // assert.IsDecreasing(t, []string{"b", "a"}) func IsDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { - return isOrdered(t, object, []CompareType{compareGreater}, "\"%v\" is not greater than \"%v\"", msgAndArgs...) + return isOrdered(t, object, []compareResult{compareGreater}, "\"%v\" is not greater than \"%v\"", msgAndArgs...) } // IsNonDecreasing asserts that the collection is not decreasing @@ -77,5 +77,5 @@ func IsDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) boo // assert.IsNonDecreasing(t, []float{1, 2}) // assert.IsNonDecreasing(t, []string{"a", "b"}) func IsNonDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { - return isOrdered(t, object, []CompareType{compareLess, compareEqual}, "\"%v\" is not less than or equal to \"%v\"", msgAndArgs...) + return isOrdered(t, object, []compareResult{compareLess, compareEqual}, "\"%v\" is not less than or equal to \"%v\"", msgAndArgs...) } diff --git a/vendor/github.com/stretchr/testify/assert/assertions.go b/vendor/github.com/stretchr/testify/assert/assertions.go index 0b7570f..4e91332 100644 --- a/vendor/github.com/stretchr/testify/assert/assertions.go +++ b/vendor/github.com/stretchr/testify/assert/assertions.go @@ -19,7 +19,9 @@ import ( "github.com/davecgh/go-spew/spew" "github.com/pmezard/go-difflib/difflib" - "gopkg.in/yaml.v3" + + // Wrapper around gopkg.in/yaml.v3 + "github.com/stretchr/testify/assert/yaml" ) //go:generate sh -c "cd ../_codegen && go build && cd - && ../_codegen/_codegen -output-package=assert -template=assertion_format.go.tmpl" @@ -45,6 +47,10 @@ type BoolAssertionFunc func(TestingT, bool, ...interface{}) bool // for table driven tests. type ErrorAssertionFunc func(TestingT, error, ...interface{}) bool +// PanicAssertionFunc is a common function prototype when validating a panic value. Can be useful +// for table driven tests. +type PanicAssertionFunc = func(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool + // Comparison is a custom function that returns true on success and false on failure type Comparison func() (success bool) @@ -496,7 +502,13 @@ func Same(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) b h.Helper() } - if !samePointers(expected, actual) { + same, ok := samePointers(expected, actual) + if !ok { + return Fail(t, "Both arguments must be pointers", msgAndArgs...) + } + + if !same { + // both are pointers but not the same type & pointing to the same address return Fail(t, fmt.Sprintf("Not same: \n"+ "expected: %p %#v\n"+ "actual : %p %#v", expected, expected, actual, actual), msgAndArgs...) @@ -516,7 +528,13 @@ func NotSame(t TestingT, expected, actual interface{}, msgAndArgs ...interface{} h.Helper() } - if samePointers(expected, actual) { + same, ok := samePointers(expected, actual) + if !ok { + //fails when the arguments are not pointers + return !(Fail(t, "Both arguments must be pointers", msgAndArgs...)) + } + + if same { return Fail(t, fmt.Sprintf( "Expected and actual point to the same object: %p %#v", expected, expected), msgAndArgs...) @@ -524,21 +542,23 @@ func NotSame(t TestingT, expected, actual interface{}, msgAndArgs ...interface{} return true } -// samePointers compares two generic interface objects and returns whether -// they point to the same object -func samePointers(first, second interface{}) bool { +// samePointers checks if two generic interface objects are pointers of the same +// type pointing to the same object. It returns two values: same indicating if +// they are the same type and point to the same object, and ok indicating that +// both inputs are pointers. +func samePointers(first, second interface{}) (same bool, ok bool) { firstPtr, secondPtr := reflect.ValueOf(first), reflect.ValueOf(second) if firstPtr.Kind() != reflect.Ptr || secondPtr.Kind() != reflect.Ptr { - return false + return false, false //not both are pointers } firstType, secondType := reflect.TypeOf(first), reflect.TypeOf(second) if firstType != secondType { - return false + return false, true // both are pointers, but of different types } // compare pointer addresses - return first == second + return first == second, true } // formatUnequalValues takes two values of arbitrary types and returns string @@ -572,8 +592,8 @@ func truncatingFormat(data interface{}) string { return value } -// EqualValues asserts that two objects are equal or convertible to the same types -// and equal. +// EqualValues asserts that two objects are equal or convertible to the larger +// type and equal. // // assert.EqualValues(t, uint32(123), int32(123)) func EqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { @@ -615,21 +635,6 @@ func EqualExportedValues(t TestingT, expected, actual interface{}, msgAndArgs .. return Fail(t, fmt.Sprintf("Types expected to match exactly\n\t%v != %v", aType, bType), msgAndArgs...) } - if aType.Kind() == reflect.Ptr { - aType = aType.Elem() - } - if bType.Kind() == reflect.Ptr { - bType = bType.Elem() - } - - if aType.Kind() != reflect.Struct { - return Fail(t, fmt.Sprintf("Types expected to both be struct or pointer to struct \n\t%v != %v", aType.Kind(), reflect.Struct), msgAndArgs...) - } - - if bType.Kind() != reflect.Struct { - return Fail(t, fmt.Sprintf("Types expected to both be struct or pointer to struct \n\t%v != %v", bType.Kind(), reflect.Struct), msgAndArgs...) - } - expected = copyExportedFields(expected) actual = copyExportedFields(actual) @@ -1170,6 +1175,39 @@ func formatListDiff(listA, listB interface{}, extraA, extraB []interface{}) stri return msg.String() } +// NotElementsMatch asserts that the specified listA(array, slice...) is NOT equal to specified +// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements, +// the number of appearances of each of them in both lists should not match. +// This is an inverse of ElementsMatch. +// +// assert.NotElementsMatch(t, [1, 1, 2, 3], [1, 1, 2, 3]) -> false +// +// assert.NotElementsMatch(t, [1, 1, 2, 3], [1, 2, 3]) -> true +// +// assert.NotElementsMatch(t, [1, 2, 3], [1, 2, 4]) -> true +func NotElementsMatch(t TestingT, listA, listB interface{}, msgAndArgs ...interface{}) (ok bool) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if isEmpty(listA) && isEmpty(listB) { + return Fail(t, "listA and listB contain the same elements", msgAndArgs) + } + + if !isList(t, listA, msgAndArgs...) { + return Fail(t, "listA is not a list type", msgAndArgs...) + } + if !isList(t, listB, msgAndArgs...) { + return Fail(t, "listB is not a list type", msgAndArgs...) + } + + extraA, extraB := diffLists(listA, listB) + if len(extraA) == 0 && len(extraB) == 0 { + return Fail(t, "listA and listB contain the same elements", msgAndArgs) + } + + return true +} + // Condition uses a Comparison to assert a complex condition. func Condition(t TestingT, comp Comparison, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { @@ -1488,6 +1526,9 @@ func InEpsilon(t TestingT, expected, actual interface{}, epsilon float64, msgAnd if err != nil { return Fail(t, err.Error(), msgAndArgs...) } + if math.IsNaN(actualEpsilon) { + return Fail(t, "relative error is NaN", msgAndArgs...) + } if actualEpsilon > epsilon { return Fail(t, fmt.Sprintf("Relative error is too high: %#v (expected)\n"+ " < %#v (actual)", epsilon, actualEpsilon), msgAndArgs...) @@ -1611,7 +1652,6 @@ func ErrorContains(t TestingT, theError error, contains string, msgAndArgs ...in // matchRegexp return true if a specified regexp matches a string. func matchRegexp(rx interface{}, str interface{}) bool { - var r *regexp.Regexp if rr, ok := rx.(*regexp.Regexp); ok { r = rr @@ -1619,7 +1659,14 @@ func matchRegexp(rx interface{}, str interface{}) bool { r = regexp.MustCompile(fmt.Sprint(rx)) } - return (r.FindStringIndex(fmt.Sprint(str)) != nil) + switch v := str.(type) { + case []byte: + return r.Match(v) + case string: + return r.MatchString(v) + default: + return r.MatchString(fmt.Sprint(v)) + } } @@ -1872,7 +1919,7 @@ var spewConfigStringerEnabled = spew.ConfigState{ MaxDepth: 10, } -type tHelper interface { +type tHelper = interface { Helper() } @@ -1911,6 +1958,9 @@ func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick t // CollectT implements the TestingT interface and collects all errors. type CollectT struct { + // A slice of errors. Non-nil slice denotes a failure. + // If it's non-nil but len(c.errors) == 0, this is also a failure + // obtained by direct c.FailNow() call. errors []error } @@ -1919,9 +1969,10 @@ func (c *CollectT) Errorf(format string, args ...interface{}) { c.errors = append(c.errors, fmt.Errorf(format, args...)) } -// FailNow panics. -func (*CollectT) FailNow() { - panic("Assertion failed") +// FailNow stops execution by calling runtime.Goexit. +func (c *CollectT) FailNow() { + c.fail() + runtime.Goexit() } // Deprecated: That was a method for internal usage that should not have been published. Now just panics. @@ -1934,6 +1985,16 @@ func (*CollectT) Copy(TestingT) { panic("Copy() is deprecated") } +func (c *CollectT) fail() { + if !c.failed() { + c.errors = []error{} // Make it non-nil to mark a failure. + } +} + +func (c *CollectT) failed() bool { + return c.errors != nil +} + // EventuallyWithT asserts that given condition will be met in waitFor time, // periodically checking target function each tick. In contrast to Eventually, // it supplies a CollectT to the condition function, so that the condition @@ -1951,14 +2012,14 @@ func (*CollectT) Copy(TestingT) { // assert.EventuallyWithT(t, func(c *assert.CollectT) { // // add assertions as needed; any assertion failure will fail the current tick // assert.True(c, externalValue, "expected 'externalValue' to be true") -// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false") +// }, 10*time.Second, 1*time.Second, "external state has not changed to 'true'; still false") func EventuallyWithT(t TestingT, condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } var lastFinishedTickErrs []error - ch := make(chan []error, 1) + ch := make(chan *CollectT, 1) timer := time.NewTimer(waitFor) defer timer.Stop() @@ -1978,16 +2039,16 @@ func EventuallyWithT(t TestingT, condition func(collect *CollectT), waitFor time go func() { collect := new(CollectT) defer func() { - ch <- collect.errors + ch <- collect }() condition(collect) }() - case errs := <-ch: - if len(errs) == 0 { + case collect := <-ch: + if !collect.failed() { return true } // Keep the errors from the last ended condition, so that they can be copied to t if timeout is reached. - lastFinishedTickErrs = errs + lastFinishedTickErrs = collect.errors tick = ticker.C } } @@ -2049,7 +2110,7 @@ func ErrorIs(t TestingT, err, target error, msgAndArgs ...interface{}) bool { ), msgAndArgs...) } -// NotErrorIs asserts that at none of the errors in err's chain matches target. +// NotErrorIs asserts that none of the errors in err's chain matches target. // This is a wrapper for errors.Is. func NotErrorIs(t TestingT, err, target error, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { @@ -2090,6 +2151,24 @@ func ErrorAs(t TestingT, err error, target interface{}, msgAndArgs ...interface{ ), msgAndArgs...) } +// NotErrorAs asserts that none of the errors in err's chain matches target, +// but if so, sets target to that error value. +func NotErrorAs(t TestingT, err error, target interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if !errors.As(err, target) { + return true + } + + chain := buildErrorChainString(err) + + return Fail(t, fmt.Sprintf("Target error should not be in err chain:\n"+ + "found: %q\n"+ + "in chain: %s", target, chain, + ), msgAndArgs...) +} + func buildErrorChainString(err error) string { if err == nil { return "" diff --git a/vendor/github.com/stretchr/testify/assert/yaml/yaml_custom.go b/vendor/github.com/stretchr/testify/assert/yaml/yaml_custom.go new file mode 100644 index 0000000..baa0cc7 --- /dev/null +++ b/vendor/github.com/stretchr/testify/assert/yaml/yaml_custom.go @@ -0,0 +1,25 @@ +//go:build testify_yaml_custom && !testify_yaml_fail && !testify_yaml_default +// +build testify_yaml_custom,!testify_yaml_fail,!testify_yaml_default + +// Package yaml is an implementation of YAML functions that calls a pluggable implementation. +// +// This implementation is selected with the testify_yaml_custom build tag. +// +// go test -tags testify_yaml_custom +// +// This implementation can be used at build time to replace the default implementation +// to avoid linking with [gopkg.in/yaml.v3]. +// +// In your test package: +// +// import assertYaml "github.com/stretchr/testify/assert/yaml" +// +// func init() { +// assertYaml.Unmarshal = func (in []byte, out interface{}) error { +// // ... +// return nil +// } +// } +package yaml + +var Unmarshal func(in []byte, out interface{}) error diff --git a/vendor/github.com/stretchr/testify/assert/yaml/yaml_default.go b/vendor/github.com/stretchr/testify/assert/yaml/yaml_default.go new file mode 100644 index 0000000..b83c6cf --- /dev/null +++ b/vendor/github.com/stretchr/testify/assert/yaml/yaml_default.go @@ -0,0 +1,37 @@ +//go:build !testify_yaml_fail && !testify_yaml_custom +// +build !testify_yaml_fail,!testify_yaml_custom + +// Package yaml is just an indirection to handle YAML deserialization. +// +// This package is just an indirection that allows the builder to override the +// indirection with an alternative implementation of this package that uses +// another implementation of YAML deserialization. This allows to not either not +// use YAML deserialization at all, or to use another implementation than +// [gopkg.in/yaml.v3] (for example for license compatibility reasons, see [PR #1120]). +// +// Alternative implementations are selected using build tags: +// +// - testify_yaml_fail: [Unmarshal] always fails with an error +// - testify_yaml_custom: [Unmarshal] is a variable. Caller must initialize it +// before calling any of [github.com/stretchr/testify/assert.YAMLEq] or +// [github.com/stretchr/testify/assert.YAMLEqf]. +// +// Usage: +// +// go test -tags testify_yaml_fail +// +// You can check with "go list" which implementation is linked: +// +// go list -f '{{.Imports}}' github.com/stretchr/testify/assert/yaml +// go list -tags testify_yaml_fail -f '{{.Imports}}' github.com/stretchr/testify/assert/yaml +// go list -tags testify_yaml_custom -f '{{.Imports}}' github.com/stretchr/testify/assert/yaml +// +// [PR #1120]: https://github.com/stretchr/testify/pull/1120 +package yaml + +import goyaml "gopkg.in/yaml.v3" + +// Unmarshal is just a wrapper of [gopkg.in/yaml.v3.Unmarshal]. +func Unmarshal(in []byte, out interface{}) error { + return goyaml.Unmarshal(in, out) +} diff --git a/vendor/github.com/stretchr/testify/assert/yaml/yaml_fail.go b/vendor/github.com/stretchr/testify/assert/yaml/yaml_fail.go new file mode 100644 index 0000000..e78f7df --- /dev/null +++ b/vendor/github.com/stretchr/testify/assert/yaml/yaml_fail.go @@ -0,0 +1,18 @@ +//go:build testify_yaml_fail && !testify_yaml_custom && !testify_yaml_default +// +build testify_yaml_fail,!testify_yaml_custom,!testify_yaml_default + +// Package yaml is an implementation of YAML functions that always fail. +// +// This implementation can be used at build time to replace the default implementation +// to avoid linking with [gopkg.in/yaml.v3]: +// +// go test -tags testify_yaml_fail +package yaml + +import "errors" + +var errNotImplemented = errors.New("YAML functions are not available (see https://pkg.go.dev/github.com/stretchr/testify/assert/yaml)") + +func Unmarshal([]byte, interface{}) error { + return errNotImplemented +} diff --git a/vendor/golang.org/x/crypto/LICENSE b/vendor/golang.org/x/crypto/LICENSE index 6a66aea..2a7cf70 100644 --- a/vendor/golang.org/x/crypto/LICENSE +++ b/vendor/golang.org/x/crypto/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2009 The Go Authors. All rights reserved. +Copyright 2009 The Go Authors. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -10,7 +10,7 @@ notice, this list of conditions and the following disclaimer. copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * Neither the name of Google Inc. nor the names of its + * Neither the name of Google LLC nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. diff --git a/vendor/golang.org/x/crypto/internal/poly1305/mac_noasm.go b/vendor/golang.org/x/crypto/internal/poly1305/mac_noasm.go index 333da28..bd896bd 100644 --- a/vendor/golang.org/x/crypto/internal/poly1305/mac_noasm.go +++ b/vendor/golang.org/x/crypto/internal/poly1305/mac_noasm.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build (!amd64 && !ppc64le && !s390x) || !gc || purego +//go:build (!amd64 && !ppc64le && !ppc64 && !s390x) || !gc || purego package poly1305 diff --git a/vendor/golang.org/x/crypto/internal/poly1305/sum_amd64.s b/vendor/golang.org/x/crypto/internal/poly1305/sum_amd64.s index e0d3c64..1337573 100644 --- a/vendor/golang.org/x/crypto/internal/poly1305/sum_amd64.s +++ b/vendor/golang.org/x/crypto/internal/poly1305/sum_amd64.s @@ -1,108 +1,93 @@ -// Copyright 2012 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. +// Code generated by command: go run sum_amd64_asm.go -out ../sum_amd64.s -pkg poly1305. DO NOT EDIT. //go:build gc && !purego -#include "textflag.h" - -#define POLY1305_ADD(msg, h0, h1, h2) \ - ADDQ 0(msg), h0; \ - ADCQ 8(msg), h1; \ - ADCQ $1, h2; \ - LEAQ 16(msg), msg - -#define POLY1305_MUL(h0, h1, h2, r0, r1, t0, t1, t2, t3) \ - MOVQ r0, AX; \ - MULQ h0; \ - MOVQ AX, t0; \ - MOVQ DX, t1; \ - MOVQ r0, AX; \ - MULQ h1; \ - ADDQ AX, t1; \ - ADCQ $0, DX; \ - MOVQ r0, t2; \ - IMULQ h2, t2; \ - ADDQ DX, t2; \ - \ - MOVQ r1, AX; \ - MULQ h0; \ - ADDQ AX, t1; \ - ADCQ $0, DX; \ - MOVQ DX, h0; \ - MOVQ r1, t3; \ - IMULQ h2, t3; \ - MOVQ r1, AX; \ - MULQ h1; \ - ADDQ AX, t2; \ - ADCQ DX, t3; \ - ADDQ h0, t2; \ - ADCQ $0, t3; \ - \ - MOVQ t0, h0; \ - MOVQ t1, h1; \ - MOVQ t2, h2; \ - ANDQ $3, h2; \ - MOVQ t2, t0; \ - ANDQ $0xFFFFFFFFFFFFFFFC, t0; \ - ADDQ t0, h0; \ - ADCQ t3, h1; \ - ADCQ $0, h2; \ - SHRQ $2, t3, t2; \ - SHRQ $2, t3; \ - ADDQ t2, h0; \ - ADCQ t3, h1; \ - ADCQ $0, h2 - -// func update(state *[7]uint64, msg []byte) +// func update(state *macState, msg []byte) TEXT ·update(SB), $0-32 MOVQ state+0(FP), DI MOVQ msg_base+8(FP), SI MOVQ msg_len+16(FP), R15 - - MOVQ 0(DI), R8 // h0 - MOVQ 8(DI), R9 // h1 - MOVQ 16(DI), R10 // h2 - MOVQ 24(DI), R11 // r0 - MOVQ 32(DI), R12 // r1 - - CMPQ R15, $16 + MOVQ (DI), R8 + MOVQ 8(DI), R9 + MOVQ 16(DI), R10 + MOVQ 24(DI), R11 + MOVQ 32(DI), R12 + CMPQ R15, $0x10 JB bytes_between_0_and_15 loop: - POLY1305_ADD(SI, R8, R9, R10) + ADDQ (SI), R8 + ADCQ 8(SI), R9 + ADCQ $0x01, R10 + LEAQ 16(SI), SI multiply: - POLY1305_MUL(R8, R9, R10, R11, R12, BX, CX, R13, R14) - SUBQ $16, R15 - CMPQ R15, $16 - JAE loop + MOVQ R11, AX + MULQ R8 + MOVQ AX, BX + MOVQ DX, CX + MOVQ R11, AX + MULQ R9 + ADDQ AX, CX + ADCQ $0x00, DX + MOVQ R11, R13 + IMULQ R10, R13 + ADDQ DX, R13 + MOVQ R12, AX + MULQ R8 + ADDQ AX, CX + ADCQ $0x00, DX + MOVQ DX, R8 + MOVQ R12, R14 + IMULQ R10, R14 + MOVQ R12, AX + MULQ R9 + ADDQ AX, R13 + ADCQ DX, R14 + ADDQ R8, R13 + ADCQ $0x00, R14 + MOVQ BX, R8 + MOVQ CX, R9 + MOVQ R13, R10 + ANDQ $0x03, R10 + MOVQ R13, BX + ANDQ $-4, BX + ADDQ BX, R8 + ADCQ R14, R9 + ADCQ $0x00, R10 + SHRQ $0x02, R14, R13 + SHRQ $0x02, R14 + ADDQ R13, R8 + ADCQ R14, R9 + ADCQ $0x00, R10 + SUBQ $0x10, R15 + CMPQ R15, $0x10 + JAE loop bytes_between_0_and_15: TESTQ R15, R15 JZ done - MOVQ $1, BX + MOVQ $0x00000001, BX XORQ CX, CX XORQ R13, R13 ADDQ R15, SI flush_buffer: - SHLQ $8, BX, CX - SHLQ $8, BX + SHLQ $0x08, BX, CX + SHLQ $0x08, BX MOVB -1(SI), R13 XORQ R13, BX DECQ SI DECQ R15 JNZ flush_buffer - ADDQ BX, R8 ADCQ CX, R9 - ADCQ $0, R10 - MOVQ $16, R15 + ADCQ $0x00, R10 + MOVQ $0x00000010, R15 JMP multiply done: - MOVQ R8, 0(DI) + MOVQ R8, (DI) MOVQ R9, 8(DI) MOVQ R10, 16(DI) RET diff --git a/vendor/golang.org/x/crypto/internal/poly1305/sum_ppc64le.go b/vendor/golang.org/x/crypto/internal/poly1305/sum_ppc64x.go similarity index 95% rename from vendor/golang.org/x/crypto/internal/poly1305/sum_ppc64le.go rename to vendor/golang.org/x/crypto/internal/poly1305/sum_ppc64x.go index 4aec487..1a1679a 100644 --- a/vendor/golang.org/x/crypto/internal/poly1305/sum_ppc64le.go +++ b/vendor/golang.org/x/crypto/internal/poly1305/sum_ppc64x.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build gc && !purego +//go:build gc && !purego && (ppc64 || ppc64le) package poly1305 diff --git a/vendor/golang.org/x/crypto/internal/poly1305/sum_ppc64le.s b/vendor/golang.org/x/crypto/internal/poly1305/sum_ppc64x.s similarity index 89% rename from vendor/golang.org/x/crypto/internal/poly1305/sum_ppc64le.s rename to vendor/golang.org/x/crypto/internal/poly1305/sum_ppc64x.s index b3c1699..6899a1d 100644 --- a/vendor/golang.org/x/crypto/internal/poly1305/sum_ppc64le.s +++ b/vendor/golang.org/x/crypto/internal/poly1305/sum_ppc64x.s @@ -2,15 +2,25 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build gc && !purego +//go:build gc && !purego && (ppc64 || ppc64le) #include "textflag.h" // This was ported from the amd64 implementation. +#ifdef GOARCH_ppc64le +#define LE_MOVD MOVD +#define LE_MOVWZ MOVWZ +#define LE_MOVHZ MOVHZ +#else +#define LE_MOVD MOVDBR +#define LE_MOVWZ MOVWBR +#define LE_MOVHZ MOVHBR +#endif + #define POLY1305_ADD(msg, h0, h1, h2, t0, t1, t2) \ - MOVD (msg), t0; \ - MOVD 8(msg), t1; \ + LE_MOVD (msg)( R0), t0; \ + LE_MOVD (msg)(R24), t1; \ MOVD $1, t2; \ ADDC t0, h0, h0; \ ADDE t1, h1, h1; \ @@ -50,10 +60,6 @@ ADDE t3, h1, h1; \ ADDZE h2 -DATA ·poly1305Mask<>+0x00(SB)/8, $0x0FFFFFFC0FFFFFFF -DATA ·poly1305Mask<>+0x08(SB)/8, $0x0FFFFFFC0FFFFFFC -GLOBL ·poly1305Mask<>(SB), RODATA, $16 - // func update(state *[7]uint64, msg []byte) TEXT ·update(SB), $0-32 MOVD state+0(FP), R3 @@ -66,6 +72,8 @@ TEXT ·update(SB), $0-32 MOVD 24(R3), R11 // r0 MOVD 32(R3), R12 // r1 + MOVD $8, R24 + CMP R5, $16 BLT bytes_between_0_and_15 @@ -94,7 +102,7 @@ flush_buffer: // Greater than 8 -- load the rightmost remaining bytes in msg // and put into R17 (h1) - MOVD (R4)(R21), R17 + LE_MOVD (R4)(R21), R17 MOVD $16, R22 // Find the offset to those bytes @@ -118,7 +126,7 @@ just1: BLT less8 // Exactly 8 - MOVD (R4), R16 + LE_MOVD (R4), R16 CMP R17, $0 @@ -133,7 +141,7 @@ less8: MOVD $0, R22 // shift count CMP R5, $4 BLT less4 - MOVWZ (R4), R16 + LE_MOVWZ (R4), R16 ADD $4, R4 ADD $-4, R5 MOVD $32, R22 @@ -141,7 +149,7 @@ less8: less4: CMP R5, $2 BLT less2 - MOVHZ (R4), R21 + LE_MOVHZ (R4), R21 SLD R22, R21, R21 OR R16, R21, R16 ADD $16, R22 diff --git a/vendor/golang.org/x/crypto/salsa20/salsa/salsa20_amd64.s b/vendor/golang.org/x/crypto/salsa20/salsa/salsa20_amd64.s index fcce023..3883e0e 100644 --- a/vendor/golang.org/x/crypto/salsa20/salsa/salsa20_amd64.s +++ b/vendor/golang.org/x/crypto/salsa20/salsa/salsa20_amd64.s @@ -1,880 +1,880 @@ -// Copyright 2012 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. +// Code generated by command: go run salsa20_amd64_asm.go -out ../salsa20_amd64.s -pkg salsa. DO NOT EDIT. //go:build amd64 && !purego && gc -// This code was translated into a form compatible with 6a from the public -// domain sources in SUPERCOP: https://bench.cr.yp.to/supercop.html +// func salsa2020XORKeyStream(out *byte, in *byte, n uint64, nonce *byte, key *byte) +// Requires: SSE2 +TEXT ·salsa2020XORKeyStream(SB), $456-40 + // This needs up to 64 bytes at 360(R12); hence the non-obvious frame size. + MOVQ out+0(FP), DI + MOVQ in+8(FP), SI + MOVQ n+16(FP), DX + MOVQ nonce+24(FP), CX + MOVQ key+32(FP), R8 + MOVQ SP, R12 + ADDQ $0x1f, R12 + ANDQ $-32, R12 + MOVQ DX, R9 + MOVQ CX, DX + MOVQ R8, R10 + CMPQ R9, $0x00 + JBE DONE + MOVL 20(R10), CX + MOVL (R10), R8 + MOVL (DX), AX + MOVL 16(R10), R11 + MOVL CX, (R12) + MOVL R8, 4(R12) + MOVL AX, 8(R12) + MOVL R11, 12(R12) + MOVL 8(DX), CX + MOVL 24(R10), R8 + MOVL 4(R10), AX + MOVL 4(DX), R11 + MOVL CX, 16(R12) + MOVL R8, 20(R12) + MOVL AX, 24(R12) + MOVL R11, 28(R12) + MOVL 12(DX), CX + MOVL 12(R10), DX + MOVL 28(R10), R8 + MOVL 8(R10), AX + MOVL DX, 32(R12) + MOVL CX, 36(R12) + MOVL R8, 40(R12) + MOVL AX, 44(R12) + MOVQ $0x61707865, DX + MOVQ $0x3320646e, CX + MOVQ $0x79622d32, R8 + MOVQ $0x6b206574, AX + MOVL DX, 48(R12) + MOVL CX, 52(R12) + MOVL R8, 56(R12) + MOVL AX, 60(R12) + CMPQ R9, $0x00000100 + JB BYTESBETWEEN1AND255 + MOVOA 48(R12), X0 + PSHUFL $0x55, X0, X1 + PSHUFL $0xaa, X0, X2 + PSHUFL $0xff, X0, X3 + PSHUFL $0x00, X0, X0 + MOVOA X1, 64(R12) + MOVOA X2, 80(R12) + MOVOA X3, 96(R12) + MOVOA X0, 112(R12) + MOVOA (R12), X0 + PSHUFL $0xaa, X0, X1 + PSHUFL $0xff, X0, X2 + PSHUFL $0x00, X0, X3 + PSHUFL $0x55, X0, X0 + MOVOA X1, 128(R12) + MOVOA X2, 144(R12) + MOVOA X3, 160(R12) + MOVOA X0, 176(R12) + MOVOA 16(R12), X0 + PSHUFL $0xff, X0, X1 + PSHUFL $0x55, X0, X2 + PSHUFL $0xaa, X0, X0 + MOVOA X1, 192(R12) + MOVOA X2, 208(R12) + MOVOA X0, 224(R12) + MOVOA 32(R12), X0 + PSHUFL $0x00, X0, X1 + PSHUFL $0xaa, X0, X2 + PSHUFL $0xff, X0, X0 + MOVOA X1, 240(R12) + MOVOA X2, 256(R12) + MOVOA X0, 272(R12) -// func salsa2020XORKeyStream(out, in *byte, n uint64, nonce, key *byte) -// This needs up to 64 bytes at 360(R12); hence the non-obvious frame size. -TEXT ·salsa2020XORKeyStream(SB),0,$456-40 // frame = 424 + 32 byte alignment - MOVQ out+0(FP),DI - MOVQ in+8(FP),SI - MOVQ n+16(FP),DX - MOVQ nonce+24(FP),CX - MOVQ key+32(FP),R8 +BYTESATLEAST256: + MOVL 16(R12), DX + MOVL 36(R12), CX + MOVL DX, 288(R12) + MOVL CX, 304(R12) + SHLQ $0x20, CX + ADDQ CX, DX + ADDQ $0x01, DX + MOVQ DX, CX + SHRQ $0x20, CX + MOVL DX, 292(R12) + MOVL CX, 308(R12) + ADDQ $0x01, DX + MOVQ DX, CX + SHRQ $0x20, CX + MOVL DX, 296(R12) + MOVL CX, 312(R12) + ADDQ $0x01, DX + MOVQ DX, CX + SHRQ $0x20, CX + MOVL DX, 300(R12) + MOVL CX, 316(R12) + ADDQ $0x01, DX + MOVQ DX, CX + SHRQ $0x20, CX + MOVL DX, 16(R12) + MOVL CX, 36(R12) + MOVQ R9, 352(R12) + MOVQ $0x00000014, DX + MOVOA 64(R12), X0 + MOVOA 80(R12), X1 + MOVOA 96(R12), X2 + MOVOA 256(R12), X3 + MOVOA 272(R12), X4 + MOVOA 128(R12), X5 + MOVOA 144(R12), X6 + MOVOA 176(R12), X7 + MOVOA 192(R12), X8 + MOVOA 208(R12), X9 + MOVOA 224(R12), X10 + MOVOA 304(R12), X11 + MOVOA 112(R12), X12 + MOVOA 160(R12), X13 + MOVOA 240(R12), X14 + MOVOA 288(R12), X15 - MOVQ SP,R12 - ADDQ $31, R12 - ANDQ $~31, R12 +MAINLOOP1: + MOVOA X1, 320(R12) + MOVOA X2, 336(R12) + MOVOA X13, X1 + PADDL X12, X1 + MOVOA X1, X2 + PSLLL $0x07, X1 + PXOR X1, X14 + PSRLL $0x19, X2 + PXOR X2, X14 + MOVOA X7, X1 + PADDL X0, X1 + MOVOA X1, X2 + PSLLL $0x07, X1 + PXOR X1, X11 + PSRLL $0x19, X2 + PXOR X2, X11 + MOVOA X12, X1 + PADDL X14, X1 + MOVOA X1, X2 + PSLLL $0x09, X1 + PXOR X1, X15 + PSRLL $0x17, X2 + PXOR X2, X15 + MOVOA X0, X1 + PADDL X11, X1 + MOVOA X1, X2 + PSLLL $0x09, X1 + PXOR X1, X9 + PSRLL $0x17, X2 + PXOR X2, X9 + MOVOA X14, X1 + PADDL X15, X1 + MOVOA X1, X2 + PSLLL $0x0d, X1 + PXOR X1, X13 + PSRLL $0x13, X2 + PXOR X2, X13 + MOVOA X11, X1 + PADDL X9, X1 + MOVOA X1, X2 + PSLLL $0x0d, X1 + PXOR X1, X7 + PSRLL $0x13, X2 + PXOR X2, X7 + MOVOA X15, X1 + PADDL X13, X1 + MOVOA X1, X2 + PSLLL $0x12, X1 + PXOR X1, X12 + PSRLL $0x0e, X2 + PXOR X2, X12 + MOVOA 320(R12), X1 + MOVOA X12, 320(R12) + MOVOA X9, X2 + PADDL X7, X2 + MOVOA X2, X12 + PSLLL $0x12, X2 + PXOR X2, X0 + PSRLL $0x0e, X12 + PXOR X12, X0 + MOVOA X5, X2 + PADDL X1, X2 + MOVOA X2, X12 + PSLLL $0x07, X2 + PXOR X2, X3 + PSRLL $0x19, X12 + PXOR X12, X3 + MOVOA 336(R12), X2 + MOVOA X0, 336(R12) + MOVOA X6, X0 + PADDL X2, X0 + MOVOA X0, X12 + PSLLL $0x07, X0 + PXOR X0, X4 + PSRLL $0x19, X12 + PXOR X12, X4 + MOVOA X1, X0 + PADDL X3, X0 + MOVOA X0, X12 + PSLLL $0x09, X0 + PXOR X0, X10 + PSRLL $0x17, X12 + PXOR X12, X10 + MOVOA X2, X0 + PADDL X4, X0 + MOVOA X0, X12 + PSLLL $0x09, X0 + PXOR X0, X8 + PSRLL $0x17, X12 + PXOR X12, X8 + MOVOA X3, X0 + PADDL X10, X0 + MOVOA X0, X12 + PSLLL $0x0d, X0 + PXOR X0, X5 + PSRLL $0x13, X12 + PXOR X12, X5 + MOVOA X4, X0 + PADDL X8, X0 + MOVOA X0, X12 + PSLLL $0x0d, X0 + PXOR X0, X6 + PSRLL $0x13, X12 + PXOR X12, X6 + MOVOA X10, X0 + PADDL X5, X0 + MOVOA X0, X12 + PSLLL $0x12, X0 + PXOR X0, X1 + PSRLL $0x0e, X12 + PXOR X12, X1 + MOVOA 320(R12), X0 + MOVOA X1, 320(R12) + MOVOA X4, X1 + PADDL X0, X1 + MOVOA X1, X12 + PSLLL $0x07, X1 + PXOR X1, X7 + PSRLL $0x19, X12 + PXOR X12, X7 + MOVOA X8, X1 + PADDL X6, X1 + MOVOA X1, X12 + PSLLL $0x12, X1 + PXOR X1, X2 + PSRLL $0x0e, X12 + PXOR X12, X2 + MOVOA 336(R12), X12 + MOVOA X2, 336(R12) + MOVOA X14, X1 + PADDL X12, X1 + MOVOA X1, X2 + PSLLL $0x07, X1 + PXOR X1, X5 + PSRLL $0x19, X2 + PXOR X2, X5 + MOVOA X0, X1 + PADDL X7, X1 + MOVOA X1, X2 + PSLLL $0x09, X1 + PXOR X1, X10 + PSRLL $0x17, X2 + PXOR X2, X10 + MOVOA X12, X1 + PADDL X5, X1 + MOVOA X1, X2 + PSLLL $0x09, X1 + PXOR X1, X8 + PSRLL $0x17, X2 + PXOR X2, X8 + MOVOA X7, X1 + PADDL X10, X1 + MOVOA X1, X2 + PSLLL $0x0d, X1 + PXOR X1, X4 + PSRLL $0x13, X2 + PXOR X2, X4 + MOVOA X5, X1 + PADDL X8, X1 + MOVOA X1, X2 + PSLLL $0x0d, X1 + PXOR X1, X14 + PSRLL $0x13, X2 + PXOR X2, X14 + MOVOA X10, X1 + PADDL X4, X1 + MOVOA X1, X2 + PSLLL $0x12, X1 + PXOR X1, X0 + PSRLL $0x0e, X2 + PXOR X2, X0 + MOVOA 320(R12), X1 + MOVOA X0, 320(R12) + MOVOA X8, X0 + PADDL X14, X0 + MOVOA X0, X2 + PSLLL $0x12, X0 + PXOR X0, X12 + PSRLL $0x0e, X2 + PXOR X2, X12 + MOVOA X11, X0 + PADDL X1, X0 + MOVOA X0, X2 + PSLLL $0x07, X0 + PXOR X0, X6 + PSRLL $0x19, X2 + PXOR X2, X6 + MOVOA 336(R12), X2 + MOVOA X12, 336(R12) + MOVOA X3, X0 + PADDL X2, X0 + MOVOA X0, X12 + PSLLL $0x07, X0 + PXOR X0, X13 + PSRLL $0x19, X12 + PXOR X12, X13 + MOVOA X1, X0 + PADDL X6, X0 + MOVOA X0, X12 + PSLLL $0x09, X0 + PXOR X0, X15 + PSRLL $0x17, X12 + PXOR X12, X15 + MOVOA X2, X0 + PADDL X13, X0 + MOVOA X0, X12 + PSLLL $0x09, X0 + PXOR X0, X9 + PSRLL $0x17, X12 + PXOR X12, X9 + MOVOA X6, X0 + PADDL X15, X0 + MOVOA X0, X12 + PSLLL $0x0d, X0 + PXOR X0, X11 + PSRLL $0x13, X12 + PXOR X12, X11 + MOVOA X13, X0 + PADDL X9, X0 + MOVOA X0, X12 + PSLLL $0x0d, X0 + PXOR X0, X3 + PSRLL $0x13, X12 + PXOR X12, X3 + MOVOA X15, X0 + PADDL X11, X0 + MOVOA X0, X12 + PSLLL $0x12, X0 + PXOR X0, X1 + PSRLL $0x0e, X12 + PXOR X12, X1 + MOVOA X9, X0 + PADDL X3, X0 + MOVOA X0, X12 + PSLLL $0x12, X0 + PXOR X0, X2 + PSRLL $0x0e, X12 + PXOR X12, X2 + MOVOA 320(R12), X12 + MOVOA 336(R12), X0 + SUBQ $0x02, DX + JA MAINLOOP1 + PADDL 112(R12), X12 + PADDL 176(R12), X7 + PADDL 224(R12), X10 + PADDL 272(R12), X4 + MOVD X12, DX + MOVD X7, CX + MOVD X10, R8 + MOVD X4, R9 + PSHUFL $0x39, X12, X12 + PSHUFL $0x39, X7, X7 + PSHUFL $0x39, X10, X10 + PSHUFL $0x39, X4, X4 + XORL (SI), DX + XORL 4(SI), CX + XORL 8(SI), R8 + XORL 12(SI), R9 + MOVL DX, (DI) + MOVL CX, 4(DI) + MOVL R8, 8(DI) + MOVL R9, 12(DI) + MOVD X12, DX + MOVD X7, CX + MOVD X10, R8 + MOVD X4, R9 + PSHUFL $0x39, X12, X12 + PSHUFL $0x39, X7, X7 + PSHUFL $0x39, X10, X10 + PSHUFL $0x39, X4, X4 + XORL 64(SI), DX + XORL 68(SI), CX + XORL 72(SI), R8 + XORL 76(SI), R9 + MOVL DX, 64(DI) + MOVL CX, 68(DI) + MOVL R8, 72(DI) + MOVL R9, 76(DI) + MOVD X12, DX + MOVD X7, CX + MOVD X10, R8 + MOVD X4, R9 + PSHUFL $0x39, X12, X12 + PSHUFL $0x39, X7, X7 + PSHUFL $0x39, X10, X10 + PSHUFL $0x39, X4, X4 + XORL 128(SI), DX + XORL 132(SI), CX + XORL 136(SI), R8 + XORL 140(SI), R9 + MOVL DX, 128(DI) + MOVL CX, 132(DI) + MOVL R8, 136(DI) + MOVL R9, 140(DI) + MOVD X12, DX + MOVD X7, CX + MOVD X10, R8 + MOVD X4, R9 + XORL 192(SI), DX + XORL 196(SI), CX + XORL 200(SI), R8 + XORL 204(SI), R9 + MOVL DX, 192(DI) + MOVL CX, 196(DI) + MOVL R8, 200(DI) + MOVL R9, 204(DI) + PADDL 240(R12), X14 + PADDL 64(R12), X0 + PADDL 128(R12), X5 + PADDL 192(R12), X8 + MOVD X14, DX + MOVD X0, CX + MOVD X5, R8 + MOVD X8, R9 + PSHUFL $0x39, X14, X14 + PSHUFL $0x39, X0, X0 + PSHUFL $0x39, X5, X5 + PSHUFL $0x39, X8, X8 + XORL 16(SI), DX + XORL 20(SI), CX + XORL 24(SI), R8 + XORL 28(SI), R9 + MOVL DX, 16(DI) + MOVL CX, 20(DI) + MOVL R8, 24(DI) + MOVL R9, 28(DI) + MOVD X14, DX + MOVD X0, CX + MOVD X5, R8 + MOVD X8, R9 + PSHUFL $0x39, X14, X14 + PSHUFL $0x39, X0, X0 + PSHUFL $0x39, X5, X5 + PSHUFL $0x39, X8, X8 + XORL 80(SI), DX + XORL 84(SI), CX + XORL 88(SI), R8 + XORL 92(SI), R9 + MOVL DX, 80(DI) + MOVL CX, 84(DI) + MOVL R8, 88(DI) + MOVL R9, 92(DI) + MOVD X14, DX + MOVD X0, CX + MOVD X5, R8 + MOVD X8, R9 + PSHUFL $0x39, X14, X14 + PSHUFL $0x39, X0, X0 + PSHUFL $0x39, X5, X5 + PSHUFL $0x39, X8, X8 + XORL 144(SI), DX + XORL 148(SI), CX + XORL 152(SI), R8 + XORL 156(SI), R9 + MOVL DX, 144(DI) + MOVL CX, 148(DI) + MOVL R8, 152(DI) + MOVL R9, 156(DI) + MOVD X14, DX + MOVD X0, CX + MOVD X5, R8 + MOVD X8, R9 + XORL 208(SI), DX + XORL 212(SI), CX + XORL 216(SI), R8 + XORL 220(SI), R9 + MOVL DX, 208(DI) + MOVL CX, 212(DI) + MOVL R8, 216(DI) + MOVL R9, 220(DI) + PADDL 288(R12), X15 + PADDL 304(R12), X11 + PADDL 80(R12), X1 + PADDL 144(R12), X6 + MOVD X15, DX + MOVD X11, CX + MOVD X1, R8 + MOVD X6, R9 + PSHUFL $0x39, X15, X15 + PSHUFL $0x39, X11, X11 + PSHUFL $0x39, X1, X1 + PSHUFL $0x39, X6, X6 + XORL 32(SI), DX + XORL 36(SI), CX + XORL 40(SI), R8 + XORL 44(SI), R9 + MOVL DX, 32(DI) + MOVL CX, 36(DI) + MOVL R8, 40(DI) + MOVL R9, 44(DI) + MOVD X15, DX + MOVD X11, CX + MOVD X1, R8 + MOVD X6, R9 + PSHUFL $0x39, X15, X15 + PSHUFL $0x39, X11, X11 + PSHUFL $0x39, X1, X1 + PSHUFL $0x39, X6, X6 + XORL 96(SI), DX + XORL 100(SI), CX + XORL 104(SI), R8 + XORL 108(SI), R9 + MOVL DX, 96(DI) + MOVL CX, 100(DI) + MOVL R8, 104(DI) + MOVL R9, 108(DI) + MOVD X15, DX + MOVD X11, CX + MOVD X1, R8 + MOVD X6, R9 + PSHUFL $0x39, X15, X15 + PSHUFL $0x39, X11, X11 + PSHUFL $0x39, X1, X1 + PSHUFL $0x39, X6, X6 + XORL 160(SI), DX + XORL 164(SI), CX + XORL 168(SI), R8 + XORL 172(SI), R9 + MOVL DX, 160(DI) + MOVL CX, 164(DI) + MOVL R8, 168(DI) + MOVL R9, 172(DI) + MOVD X15, DX + MOVD X11, CX + MOVD X1, R8 + MOVD X6, R9 + XORL 224(SI), DX + XORL 228(SI), CX + XORL 232(SI), R8 + XORL 236(SI), R9 + MOVL DX, 224(DI) + MOVL CX, 228(DI) + MOVL R8, 232(DI) + MOVL R9, 236(DI) + PADDL 160(R12), X13 + PADDL 208(R12), X9 + PADDL 256(R12), X3 + PADDL 96(R12), X2 + MOVD X13, DX + MOVD X9, CX + MOVD X3, R8 + MOVD X2, R9 + PSHUFL $0x39, X13, X13 + PSHUFL $0x39, X9, X9 + PSHUFL $0x39, X3, X3 + PSHUFL $0x39, X2, X2 + XORL 48(SI), DX + XORL 52(SI), CX + XORL 56(SI), R8 + XORL 60(SI), R9 + MOVL DX, 48(DI) + MOVL CX, 52(DI) + MOVL R8, 56(DI) + MOVL R9, 60(DI) + MOVD X13, DX + MOVD X9, CX + MOVD X3, R8 + MOVD X2, R9 + PSHUFL $0x39, X13, X13 + PSHUFL $0x39, X9, X9 + PSHUFL $0x39, X3, X3 + PSHUFL $0x39, X2, X2 + XORL 112(SI), DX + XORL 116(SI), CX + XORL 120(SI), R8 + XORL 124(SI), R9 + MOVL DX, 112(DI) + MOVL CX, 116(DI) + MOVL R8, 120(DI) + MOVL R9, 124(DI) + MOVD X13, DX + MOVD X9, CX + MOVD X3, R8 + MOVD X2, R9 + PSHUFL $0x39, X13, X13 + PSHUFL $0x39, X9, X9 + PSHUFL $0x39, X3, X3 + PSHUFL $0x39, X2, X2 + XORL 176(SI), DX + XORL 180(SI), CX + XORL 184(SI), R8 + XORL 188(SI), R9 + MOVL DX, 176(DI) + MOVL CX, 180(DI) + MOVL R8, 184(DI) + MOVL R9, 188(DI) + MOVD X13, DX + MOVD X9, CX + MOVD X3, R8 + MOVD X2, R9 + XORL 240(SI), DX + XORL 244(SI), CX + XORL 248(SI), R8 + XORL 252(SI), R9 + MOVL DX, 240(DI) + MOVL CX, 244(DI) + MOVL R8, 248(DI) + MOVL R9, 252(DI) + MOVQ 352(R12), R9 + SUBQ $0x00000100, R9 + ADDQ $0x00000100, SI + ADDQ $0x00000100, DI + CMPQ R9, $0x00000100 + JAE BYTESATLEAST256 + CMPQ R9, $0x00 + JBE DONE - MOVQ DX,R9 - MOVQ CX,DX - MOVQ R8,R10 - CMPQ R9,$0 - JBE DONE - START: - MOVL 20(R10),CX - MOVL 0(R10),R8 - MOVL 0(DX),AX - MOVL 16(R10),R11 - MOVL CX,0(R12) - MOVL R8, 4 (R12) - MOVL AX, 8 (R12) - MOVL R11, 12 (R12) - MOVL 8(DX),CX - MOVL 24(R10),R8 - MOVL 4(R10),AX - MOVL 4(DX),R11 - MOVL CX,16(R12) - MOVL R8, 20 (R12) - MOVL AX, 24 (R12) - MOVL R11, 28 (R12) - MOVL 12(DX),CX - MOVL 12(R10),DX - MOVL 28(R10),R8 - MOVL 8(R10),AX - MOVL DX,32(R12) - MOVL CX, 36 (R12) - MOVL R8, 40 (R12) - MOVL AX, 44 (R12) - MOVQ $1634760805,DX - MOVQ $857760878,CX - MOVQ $2036477234,R8 - MOVQ $1797285236,AX - MOVL DX,48(R12) - MOVL CX, 52 (R12) - MOVL R8, 56 (R12) - MOVL AX, 60 (R12) - CMPQ R9,$256 - JB BYTESBETWEEN1AND255 - MOVOA 48(R12),X0 - PSHUFL $0X55,X0,X1 - PSHUFL $0XAA,X0,X2 - PSHUFL $0XFF,X0,X3 - PSHUFL $0X00,X0,X0 - MOVOA X1,64(R12) - MOVOA X2,80(R12) - MOVOA X3,96(R12) - MOVOA X0,112(R12) - MOVOA 0(R12),X0 - PSHUFL $0XAA,X0,X1 - PSHUFL $0XFF,X0,X2 - PSHUFL $0X00,X0,X3 - PSHUFL $0X55,X0,X0 - MOVOA X1,128(R12) - MOVOA X2,144(R12) - MOVOA X3,160(R12) - MOVOA X0,176(R12) - MOVOA 16(R12),X0 - PSHUFL $0XFF,X0,X1 - PSHUFL $0X55,X0,X2 - PSHUFL $0XAA,X0,X0 - MOVOA X1,192(R12) - MOVOA X2,208(R12) - MOVOA X0,224(R12) - MOVOA 32(R12),X0 - PSHUFL $0X00,X0,X1 - PSHUFL $0XAA,X0,X2 - PSHUFL $0XFF,X0,X0 - MOVOA X1,240(R12) - MOVOA X2,256(R12) - MOVOA X0,272(R12) - BYTESATLEAST256: - MOVL 16(R12),DX - MOVL 36 (R12),CX - MOVL DX,288(R12) - MOVL CX,304(R12) - SHLQ $32,CX - ADDQ CX,DX - ADDQ $1,DX - MOVQ DX,CX - SHRQ $32,CX - MOVL DX, 292 (R12) - MOVL CX, 308 (R12) - ADDQ $1,DX - MOVQ DX,CX - SHRQ $32,CX - MOVL DX, 296 (R12) - MOVL CX, 312 (R12) - ADDQ $1,DX - MOVQ DX,CX - SHRQ $32,CX - MOVL DX, 300 (R12) - MOVL CX, 316 (R12) - ADDQ $1,DX - MOVQ DX,CX - SHRQ $32,CX - MOVL DX,16(R12) - MOVL CX, 36 (R12) - MOVQ R9,352(R12) - MOVQ $20,DX - MOVOA 64(R12),X0 - MOVOA 80(R12),X1 - MOVOA 96(R12),X2 - MOVOA 256(R12),X3 - MOVOA 272(R12),X4 - MOVOA 128(R12),X5 - MOVOA 144(R12),X6 - MOVOA 176(R12),X7 - MOVOA 192(R12),X8 - MOVOA 208(R12),X9 - MOVOA 224(R12),X10 - MOVOA 304(R12),X11 - MOVOA 112(R12),X12 - MOVOA 160(R12),X13 - MOVOA 240(R12),X14 - MOVOA 288(R12),X15 - MAINLOOP1: - MOVOA X1,320(R12) - MOVOA X2,336(R12) - MOVOA X13,X1 - PADDL X12,X1 - MOVOA X1,X2 - PSLLL $7,X1 - PXOR X1,X14 - PSRLL $25,X2 - PXOR X2,X14 - MOVOA X7,X1 - PADDL X0,X1 - MOVOA X1,X2 - PSLLL $7,X1 - PXOR X1,X11 - PSRLL $25,X2 - PXOR X2,X11 - MOVOA X12,X1 - PADDL X14,X1 - MOVOA X1,X2 - PSLLL $9,X1 - PXOR X1,X15 - PSRLL $23,X2 - PXOR X2,X15 - MOVOA X0,X1 - PADDL X11,X1 - MOVOA X1,X2 - PSLLL $9,X1 - PXOR X1,X9 - PSRLL $23,X2 - PXOR X2,X9 - MOVOA X14,X1 - PADDL X15,X1 - MOVOA X1,X2 - PSLLL $13,X1 - PXOR X1,X13 - PSRLL $19,X2 - PXOR X2,X13 - MOVOA X11,X1 - PADDL X9,X1 - MOVOA X1,X2 - PSLLL $13,X1 - PXOR X1,X7 - PSRLL $19,X2 - PXOR X2,X7 - MOVOA X15,X1 - PADDL X13,X1 - MOVOA X1,X2 - PSLLL $18,X1 - PXOR X1,X12 - PSRLL $14,X2 - PXOR X2,X12 - MOVOA 320(R12),X1 - MOVOA X12,320(R12) - MOVOA X9,X2 - PADDL X7,X2 - MOVOA X2,X12 - PSLLL $18,X2 - PXOR X2,X0 - PSRLL $14,X12 - PXOR X12,X0 - MOVOA X5,X2 - PADDL X1,X2 - MOVOA X2,X12 - PSLLL $7,X2 - PXOR X2,X3 - PSRLL $25,X12 - PXOR X12,X3 - MOVOA 336(R12),X2 - MOVOA X0,336(R12) - MOVOA X6,X0 - PADDL X2,X0 - MOVOA X0,X12 - PSLLL $7,X0 - PXOR X0,X4 - PSRLL $25,X12 - PXOR X12,X4 - MOVOA X1,X0 - PADDL X3,X0 - MOVOA X0,X12 - PSLLL $9,X0 - PXOR X0,X10 - PSRLL $23,X12 - PXOR X12,X10 - MOVOA X2,X0 - PADDL X4,X0 - MOVOA X0,X12 - PSLLL $9,X0 - PXOR X0,X8 - PSRLL $23,X12 - PXOR X12,X8 - MOVOA X3,X0 - PADDL X10,X0 - MOVOA X0,X12 - PSLLL $13,X0 - PXOR X0,X5 - PSRLL $19,X12 - PXOR X12,X5 - MOVOA X4,X0 - PADDL X8,X0 - MOVOA X0,X12 - PSLLL $13,X0 - PXOR X0,X6 - PSRLL $19,X12 - PXOR X12,X6 - MOVOA X10,X0 - PADDL X5,X0 - MOVOA X0,X12 - PSLLL $18,X0 - PXOR X0,X1 - PSRLL $14,X12 - PXOR X12,X1 - MOVOA 320(R12),X0 - MOVOA X1,320(R12) - MOVOA X4,X1 - PADDL X0,X1 - MOVOA X1,X12 - PSLLL $7,X1 - PXOR X1,X7 - PSRLL $25,X12 - PXOR X12,X7 - MOVOA X8,X1 - PADDL X6,X1 - MOVOA X1,X12 - PSLLL $18,X1 - PXOR X1,X2 - PSRLL $14,X12 - PXOR X12,X2 - MOVOA 336(R12),X12 - MOVOA X2,336(R12) - MOVOA X14,X1 - PADDL X12,X1 - MOVOA X1,X2 - PSLLL $7,X1 - PXOR X1,X5 - PSRLL $25,X2 - PXOR X2,X5 - MOVOA X0,X1 - PADDL X7,X1 - MOVOA X1,X2 - PSLLL $9,X1 - PXOR X1,X10 - PSRLL $23,X2 - PXOR X2,X10 - MOVOA X12,X1 - PADDL X5,X1 - MOVOA X1,X2 - PSLLL $9,X1 - PXOR X1,X8 - PSRLL $23,X2 - PXOR X2,X8 - MOVOA X7,X1 - PADDL X10,X1 - MOVOA X1,X2 - PSLLL $13,X1 - PXOR X1,X4 - PSRLL $19,X2 - PXOR X2,X4 - MOVOA X5,X1 - PADDL X8,X1 - MOVOA X1,X2 - PSLLL $13,X1 - PXOR X1,X14 - PSRLL $19,X2 - PXOR X2,X14 - MOVOA X10,X1 - PADDL X4,X1 - MOVOA X1,X2 - PSLLL $18,X1 - PXOR X1,X0 - PSRLL $14,X2 - PXOR X2,X0 - MOVOA 320(R12),X1 - MOVOA X0,320(R12) - MOVOA X8,X0 - PADDL X14,X0 - MOVOA X0,X2 - PSLLL $18,X0 - PXOR X0,X12 - PSRLL $14,X2 - PXOR X2,X12 - MOVOA X11,X0 - PADDL X1,X0 - MOVOA X0,X2 - PSLLL $7,X0 - PXOR X0,X6 - PSRLL $25,X2 - PXOR X2,X6 - MOVOA 336(R12),X2 - MOVOA X12,336(R12) - MOVOA X3,X0 - PADDL X2,X0 - MOVOA X0,X12 - PSLLL $7,X0 - PXOR X0,X13 - PSRLL $25,X12 - PXOR X12,X13 - MOVOA X1,X0 - PADDL X6,X0 - MOVOA X0,X12 - PSLLL $9,X0 - PXOR X0,X15 - PSRLL $23,X12 - PXOR X12,X15 - MOVOA X2,X0 - PADDL X13,X0 - MOVOA X0,X12 - PSLLL $9,X0 - PXOR X0,X9 - PSRLL $23,X12 - PXOR X12,X9 - MOVOA X6,X0 - PADDL X15,X0 - MOVOA X0,X12 - PSLLL $13,X0 - PXOR X0,X11 - PSRLL $19,X12 - PXOR X12,X11 - MOVOA X13,X0 - PADDL X9,X0 - MOVOA X0,X12 - PSLLL $13,X0 - PXOR X0,X3 - PSRLL $19,X12 - PXOR X12,X3 - MOVOA X15,X0 - PADDL X11,X0 - MOVOA X0,X12 - PSLLL $18,X0 - PXOR X0,X1 - PSRLL $14,X12 - PXOR X12,X1 - MOVOA X9,X0 - PADDL X3,X0 - MOVOA X0,X12 - PSLLL $18,X0 - PXOR X0,X2 - PSRLL $14,X12 - PXOR X12,X2 - MOVOA 320(R12),X12 - MOVOA 336(R12),X0 - SUBQ $2,DX - JA MAINLOOP1 - PADDL 112(R12),X12 - PADDL 176(R12),X7 - PADDL 224(R12),X10 - PADDL 272(R12),X4 - MOVD X12,DX - MOVD X7,CX - MOVD X10,R8 - MOVD X4,R9 - PSHUFL $0X39,X12,X12 - PSHUFL $0X39,X7,X7 - PSHUFL $0X39,X10,X10 - PSHUFL $0X39,X4,X4 - XORL 0(SI),DX - XORL 4(SI),CX - XORL 8(SI),R8 - XORL 12(SI),R9 - MOVL DX,0(DI) - MOVL CX,4(DI) - MOVL R8,8(DI) - MOVL R9,12(DI) - MOVD X12,DX - MOVD X7,CX - MOVD X10,R8 - MOVD X4,R9 - PSHUFL $0X39,X12,X12 - PSHUFL $0X39,X7,X7 - PSHUFL $0X39,X10,X10 - PSHUFL $0X39,X4,X4 - XORL 64(SI),DX - XORL 68(SI),CX - XORL 72(SI),R8 - XORL 76(SI),R9 - MOVL DX,64(DI) - MOVL CX,68(DI) - MOVL R8,72(DI) - MOVL R9,76(DI) - MOVD X12,DX - MOVD X7,CX - MOVD X10,R8 - MOVD X4,R9 - PSHUFL $0X39,X12,X12 - PSHUFL $0X39,X7,X7 - PSHUFL $0X39,X10,X10 - PSHUFL $0X39,X4,X4 - XORL 128(SI),DX - XORL 132(SI),CX - XORL 136(SI),R8 - XORL 140(SI),R9 - MOVL DX,128(DI) - MOVL CX,132(DI) - MOVL R8,136(DI) - MOVL R9,140(DI) - MOVD X12,DX - MOVD X7,CX - MOVD X10,R8 - MOVD X4,R9 - XORL 192(SI),DX - XORL 196(SI),CX - XORL 200(SI),R8 - XORL 204(SI),R9 - MOVL DX,192(DI) - MOVL CX,196(DI) - MOVL R8,200(DI) - MOVL R9,204(DI) - PADDL 240(R12),X14 - PADDL 64(R12),X0 - PADDL 128(R12),X5 - PADDL 192(R12),X8 - MOVD X14,DX - MOVD X0,CX - MOVD X5,R8 - MOVD X8,R9 - PSHUFL $0X39,X14,X14 - PSHUFL $0X39,X0,X0 - PSHUFL $0X39,X5,X5 - PSHUFL $0X39,X8,X8 - XORL 16(SI),DX - XORL 20(SI),CX - XORL 24(SI),R8 - XORL 28(SI),R9 - MOVL DX,16(DI) - MOVL CX,20(DI) - MOVL R8,24(DI) - MOVL R9,28(DI) - MOVD X14,DX - MOVD X0,CX - MOVD X5,R8 - MOVD X8,R9 - PSHUFL $0X39,X14,X14 - PSHUFL $0X39,X0,X0 - PSHUFL $0X39,X5,X5 - PSHUFL $0X39,X8,X8 - XORL 80(SI),DX - XORL 84(SI),CX - XORL 88(SI),R8 - XORL 92(SI),R9 - MOVL DX,80(DI) - MOVL CX,84(DI) - MOVL R8,88(DI) - MOVL R9,92(DI) - MOVD X14,DX - MOVD X0,CX - MOVD X5,R8 - MOVD X8,R9 - PSHUFL $0X39,X14,X14 - PSHUFL $0X39,X0,X0 - PSHUFL $0X39,X5,X5 - PSHUFL $0X39,X8,X8 - XORL 144(SI),DX - XORL 148(SI),CX - XORL 152(SI),R8 - XORL 156(SI),R9 - MOVL DX,144(DI) - MOVL CX,148(DI) - MOVL R8,152(DI) - MOVL R9,156(DI) - MOVD X14,DX - MOVD X0,CX - MOVD X5,R8 - MOVD X8,R9 - XORL 208(SI),DX - XORL 212(SI),CX - XORL 216(SI),R8 - XORL 220(SI),R9 - MOVL DX,208(DI) - MOVL CX,212(DI) - MOVL R8,216(DI) - MOVL R9,220(DI) - PADDL 288(R12),X15 - PADDL 304(R12),X11 - PADDL 80(R12),X1 - PADDL 144(R12),X6 - MOVD X15,DX - MOVD X11,CX - MOVD X1,R8 - MOVD X6,R9 - PSHUFL $0X39,X15,X15 - PSHUFL $0X39,X11,X11 - PSHUFL $0X39,X1,X1 - PSHUFL $0X39,X6,X6 - XORL 32(SI),DX - XORL 36(SI),CX - XORL 40(SI),R8 - XORL 44(SI),R9 - MOVL DX,32(DI) - MOVL CX,36(DI) - MOVL R8,40(DI) - MOVL R9,44(DI) - MOVD X15,DX - MOVD X11,CX - MOVD X1,R8 - MOVD X6,R9 - PSHUFL $0X39,X15,X15 - PSHUFL $0X39,X11,X11 - PSHUFL $0X39,X1,X1 - PSHUFL $0X39,X6,X6 - XORL 96(SI),DX - XORL 100(SI),CX - XORL 104(SI),R8 - XORL 108(SI),R9 - MOVL DX,96(DI) - MOVL CX,100(DI) - MOVL R8,104(DI) - MOVL R9,108(DI) - MOVD X15,DX - MOVD X11,CX - MOVD X1,R8 - MOVD X6,R9 - PSHUFL $0X39,X15,X15 - PSHUFL $0X39,X11,X11 - PSHUFL $0X39,X1,X1 - PSHUFL $0X39,X6,X6 - XORL 160(SI),DX - XORL 164(SI),CX - XORL 168(SI),R8 - XORL 172(SI),R9 - MOVL DX,160(DI) - MOVL CX,164(DI) - MOVL R8,168(DI) - MOVL R9,172(DI) - MOVD X15,DX - MOVD X11,CX - MOVD X1,R8 - MOVD X6,R9 - XORL 224(SI),DX - XORL 228(SI),CX - XORL 232(SI),R8 - XORL 236(SI),R9 - MOVL DX,224(DI) - MOVL CX,228(DI) - MOVL R8,232(DI) - MOVL R9,236(DI) - PADDL 160(R12),X13 - PADDL 208(R12),X9 - PADDL 256(R12),X3 - PADDL 96(R12),X2 - MOVD X13,DX - MOVD X9,CX - MOVD X3,R8 - MOVD X2,R9 - PSHUFL $0X39,X13,X13 - PSHUFL $0X39,X9,X9 - PSHUFL $0X39,X3,X3 - PSHUFL $0X39,X2,X2 - XORL 48(SI),DX - XORL 52(SI),CX - XORL 56(SI),R8 - XORL 60(SI),R9 - MOVL DX,48(DI) - MOVL CX,52(DI) - MOVL R8,56(DI) - MOVL R9,60(DI) - MOVD X13,DX - MOVD X9,CX - MOVD X3,R8 - MOVD X2,R9 - PSHUFL $0X39,X13,X13 - PSHUFL $0X39,X9,X9 - PSHUFL $0X39,X3,X3 - PSHUFL $0X39,X2,X2 - XORL 112(SI),DX - XORL 116(SI),CX - XORL 120(SI),R8 - XORL 124(SI),R9 - MOVL DX,112(DI) - MOVL CX,116(DI) - MOVL R8,120(DI) - MOVL R9,124(DI) - MOVD X13,DX - MOVD X9,CX - MOVD X3,R8 - MOVD X2,R9 - PSHUFL $0X39,X13,X13 - PSHUFL $0X39,X9,X9 - PSHUFL $0X39,X3,X3 - PSHUFL $0X39,X2,X2 - XORL 176(SI),DX - XORL 180(SI),CX - XORL 184(SI),R8 - XORL 188(SI),R9 - MOVL DX,176(DI) - MOVL CX,180(DI) - MOVL R8,184(DI) - MOVL R9,188(DI) - MOVD X13,DX - MOVD X9,CX - MOVD X3,R8 - MOVD X2,R9 - XORL 240(SI),DX - XORL 244(SI),CX - XORL 248(SI),R8 - XORL 252(SI),R9 - MOVL DX,240(DI) - MOVL CX,244(DI) - MOVL R8,248(DI) - MOVL R9,252(DI) - MOVQ 352(R12),R9 - SUBQ $256,R9 - ADDQ $256,SI - ADDQ $256,DI - CMPQ R9,$256 - JAE BYTESATLEAST256 - CMPQ R9,$0 - JBE DONE - BYTESBETWEEN1AND255: - CMPQ R9,$64 - JAE NOCOPY - MOVQ DI,DX - LEAQ 360(R12),DI - MOVQ R9,CX +BYTESBETWEEN1AND255: + CMPQ R9, $0x40 + JAE NOCOPY + MOVQ DI, DX + LEAQ 360(R12), DI + MOVQ R9, CX REP; MOVSB - LEAQ 360(R12),DI - LEAQ 360(R12),SI - NOCOPY: - MOVQ R9,352(R12) - MOVOA 48(R12),X0 - MOVOA 0(R12),X1 - MOVOA 16(R12),X2 - MOVOA 32(R12),X3 - MOVOA X1,X4 - MOVQ $20,CX - MAINLOOP2: - PADDL X0,X4 - MOVOA X0,X5 - MOVOA X4,X6 - PSLLL $7,X4 - PSRLL $25,X6 - PXOR X4,X3 - PXOR X6,X3 - PADDL X3,X5 - MOVOA X3,X4 - MOVOA X5,X6 - PSLLL $9,X5 - PSRLL $23,X6 - PXOR X5,X2 - PSHUFL $0X93,X3,X3 - PXOR X6,X2 - PADDL X2,X4 - MOVOA X2,X5 - MOVOA X4,X6 - PSLLL $13,X4 - PSRLL $19,X6 - PXOR X4,X1 - PSHUFL $0X4E,X2,X2 - PXOR X6,X1 - PADDL X1,X5 - MOVOA X3,X4 - MOVOA X5,X6 - PSLLL $18,X5 - PSRLL $14,X6 - PXOR X5,X0 - PSHUFL $0X39,X1,X1 - PXOR X6,X0 - PADDL X0,X4 - MOVOA X0,X5 - MOVOA X4,X6 - PSLLL $7,X4 - PSRLL $25,X6 - PXOR X4,X1 - PXOR X6,X1 - PADDL X1,X5 - MOVOA X1,X4 - MOVOA X5,X6 - PSLLL $9,X5 - PSRLL $23,X6 - PXOR X5,X2 - PSHUFL $0X93,X1,X1 - PXOR X6,X2 - PADDL X2,X4 - MOVOA X2,X5 - MOVOA X4,X6 - PSLLL $13,X4 - PSRLL $19,X6 - PXOR X4,X3 - PSHUFL $0X4E,X2,X2 - PXOR X6,X3 - PADDL X3,X5 - MOVOA X1,X4 - MOVOA X5,X6 - PSLLL $18,X5 - PSRLL $14,X6 - PXOR X5,X0 - PSHUFL $0X39,X3,X3 - PXOR X6,X0 - PADDL X0,X4 - MOVOA X0,X5 - MOVOA X4,X6 - PSLLL $7,X4 - PSRLL $25,X6 - PXOR X4,X3 - PXOR X6,X3 - PADDL X3,X5 - MOVOA X3,X4 - MOVOA X5,X6 - PSLLL $9,X5 - PSRLL $23,X6 - PXOR X5,X2 - PSHUFL $0X93,X3,X3 - PXOR X6,X2 - PADDL X2,X4 - MOVOA X2,X5 - MOVOA X4,X6 - PSLLL $13,X4 - PSRLL $19,X6 - PXOR X4,X1 - PSHUFL $0X4E,X2,X2 - PXOR X6,X1 - PADDL X1,X5 - MOVOA X3,X4 - MOVOA X5,X6 - PSLLL $18,X5 - PSRLL $14,X6 - PXOR X5,X0 - PSHUFL $0X39,X1,X1 - PXOR X6,X0 - PADDL X0,X4 - MOVOA X0,X5 - MOVOA X4,X6 - PSLLL $7,X4 - PSRLL $25,X6 - PXOR X4,X1 - PXOR X6,X1 - PADDL X1,X5 - MOVOA X1,X4 - MOVOA X5,X6 - PSLLL $9,X5 - PSRLL $23,X6 - PXOR X5,X2 - PSHUFL $0X93,X1,X1 - PXOR X6,X2 - PADDL X2,X4 - MOVOA X2,X5 - MOVOA X4,X6 - PSLLL $13,X4 - PSRLL $19,X6 - PXOR X4,X3 - PSHUFL $0X4E,X2,X2 - PXOR X6,X3 - SUBQ $4,CX - PADDL X3,X5 - MOVOA X1,X4 - MOVOA X5,X6 - PSLLL $18,X5 - PXOR X7,X7 - PSRLL $14,X6 - PXOR X5,X0 - PSHUFL $0X39,X3,X3 - PXOR X6,X0 - JA MAINLOOP2 - PADDL 48(R12),X0 - PADDL 0(R12),X1 - PADDL 16(R12),X2 - PADDL 32(R12),X3 - MOVD X0,CX - MOVD X1,R8 - MOVD X2,R9 - MOVD X3,AX - PSHUFL $0X39,X0,X0 - PSHUFL $0X39,X1,X1 - PSHUFL $0X39,X2,X2 - PSHUFL $0X39,X3,X3 - XORL 0(SI),CX - XORL 48(SI),R8 - XORL 32(SI),R9 - XORL 16(SI),AX - MOVL CX,0(DI) - MOVL R8,48(DI) - MOVL R9,32(DI) - MOVL AX,16(DI) - MOVD X0,CX - MOVD X1,R8 - MOVD X2,R9 - MOVD X3,AX - PSHUFL $0X39,X0,X0 - PSHUFL $0X39,X1,X1 - PSHUFL $0X39,X2,X2 - PSHUFL $0X39,X3,X3 - XORL 20(SI),CX - XORL 4(SI),R8 - XORL 52(SI),R9 - XORL 36(SI),AX - MOVL CX,20(DI) - MOVL R8,4(DI) - MOVL R9,52(DI) - MOVL AX,36(DI) - MOVD X0,CX - MOVD X1,R8 - MOVD X2,R9 - MOVD X3,AX - PSHUFL $0X39,X0,X0 - PSHUFL $0X39,X1,X1 - PSHUFL $0X39,X2,X2 - PSHUFL $0X39,X3,X3 - XORL 40(SI),CX - XORL 24(SI),R8 - XORL 8(SI),R9 - XORL 56(SI),AX - MOVL CX,40(DI) - MOVL R8,24(DI) - MOVL R9,8(DI) - MOVL AX,56(DI) - MOVD X0,CX - MOVD X1,R8 - MOVD X2,R9 - MOVD X3,AX - XORL 60(SI),CX - XORL 44(SI),R8 - XORL 28(SI),R9 - XORL 12(SI),AX - MOVL CX,60(DI) - MOVL R8,44(DI) - MOVL R9,28(DI) - MOVL AX,12(DI) - MOVQ 352(R12),R9 - MOVL 16(R12),CX - MOVL 36 (R12),R8 - ADDQ $1,CX - SHLQ $32,R8 - ADDQ R8,CX - MOVQ CX,R8 - SHRQ $32,R8 - MOVL CX,16(R12) - MOVL R8, 36 (R12) - CMPQ R9,$64 - JA BYTESATLEAST65 - JAE BYTESATLEAST64 - MOVQ DI,SI - MOVQ DX,DI - MOVQ R9,CX + LEAQ 360(R12), DI + LEAQ 360(R12), SI + +NOCOPY: + MOVQ R9, 352(R12) + MOVOA 48(R12), X0 + MOVOA (R12), X1 + MOVOA 16(R12), X2 + MOVOA 32(R12), X3 + MOVOA X1, X4 + MOVQ $0x00000014, CX + +MAINLOOP2: + PADDL X0, X4 + MOVOA X0, X5 + MOVOA X4, X6 + PSLLL $0x07, X4 + PSRLL $0x19, X6 + PXOR X4, X3 + PXOR X6, X3 + PADDL X3, X5 + MOVOA X3, X4 + MOVOA X5, X6 + PSLLL $0x09, X5 + PSRLL $0x17, X6 + PXOR X5, X2 + PSHUFL $0x93, X3, X3 + PXOR X6, X2 + PADDL X2, X4 + MOVOA X2, X5 + MOVOA X4, X6 + PSLLL $0x0d, X4 + PSRLL $0x13, X6 + PXOR X4, X1 + PSHUFL $0x4e, X2, X2 + PXOR X6, X1 + PADDL X1, X5 + MOVOA X3, X4 + MOVOA X5, X6 + PSLLL $0x12, X5 + PSRLL $0x0e, X6 + PXOR X5, X0 + PSHUFL $0x39, X1, X1 + PXOR X6, X0 + PADDL X0, X4 + MOVOA X0, X5 + MOVOA X4, X6 + PSLLL $0x07, X4 + PSRLL $0x19, X6 + PXOR X4, X1 + PXOR X6, X1 + PADDL X1, X5 + MOVOA X1, X4 + MOVOA X5, X6 + PSLLL $0x09, X5 + PSRLL $0x17, X6 + PXOR X5, X2 + PSHUFL $0x93, X1, X1 + PXOR X6, X2 + PADDL X2, X4 + MOVOA X2, X5 + MOVOA X4, X6 + PSLLL $0x0d, X4 + PSRLL $0x13, X6 + PXOR X4, X3 + PSHUFL $0x4e, X2, X2 + PXOR X6, X3 + PADDL X3, X5 + MOVOA X1, X4 + MOVOA X5, X6 + PSLLL $0x12, X5 + PSRLL $0x0e, X6 + PXOR X5, X0 + PSHUFL $0x39, X3, X3 + PXOR X6, X0 + PADDL X0, X4 + MOVOA X0, X5 + MOVOA X4, X6 + PSLLL $0x07, X4 + PSRLL $0x19, X6 + PXOR X4, X3 + PXOR X6, X3 + PADDL X3, X5 + MOVOA X3, X4 + MOVOA X5, X6 + PSLLL $0x09, X5 + PSRLL $0x17, X6 + PXOR X5, X2 + PSHUFL $0x93, X3, X3 + PXOR X6, X2 + PADDL X2, X4 + MOVOA X2, X5 + MOVOA X4, X6 + PSLLL $0x0d, X4 + PSRLL $0x13, X6 + PXOR X4, X1 + PSHUFL $0x4e, X2, X2 + PXOR X6, X1 + PADDL X1, X5 + MOVOA X3, X4 + MOVOA X5, X6 + PSLLL $0x12, X5 + PSRLL $0x0e, X6 + PXOR X5, X0 + PSHUFL $0x39, X1, X1 + PXOR X6, X0 + PADDL X0, X4 + MOVOA X0, X5 + MOVOA X4, X6 + PSLLL $0x07, X4 + PSRLL $0x19, X6 + PXOR X4, X1 + PXOR X6, X1 + PADDL X1, X5 + MOVOA X1, X4 + MOVOA X5, X6 + PSLLL $0x09, X5 + PSRLL $0x17, X6 + PXOR X5, X2 + PSHUFL $0x93, X1, X1 + PXOR X6, X2 + PADDL X2, X4 + MOVOA X2, X5 + MOVOA X4, X6 + PSLLL $0x0d, X4 + PSRLL $0x13, X6 + PXOR X4, X3 + PSHUFL $0x4e, X2, X2 + PXOR X6, X3 + SUBQ $0x04, CX + PADDL X3, X5 + MOVOA X1, X4 + MOVOA X5, X6 + PSLLL $0x12, X5 + PXOR X7, X7 + PSRLL $0x0e, X6 + PXOR X5, X0 + PSHUFL $0x39, X3, X3 + PXOR X6, X0 + JA MAINLOOP2 + PADDL 48(R12), X0 + PADDL (R12), X1 + PADDL 16(R12), X2 + PADDL 32(R12), X3 + MOVD X0, CX + MOVD X1, R8 + MOVD X2, R9 + MOVD X3, AX + PSHUFL $0x39, X0, X0 + PSHUFL $0x39, X1, X1 + PSHUFL $0x39, X2, X2 + PSHUFL $0x39, X3, X3 + XORL (SI), CX + XORL 48(SI), R8 + XORL 32(SI), R9 + XORL 16(SI), AX + MOVL CX, (DI) + MOVL R8, 48(DI) + MOVL R9, 32(DI) + MOVL AX, 16(DI) + MOVD X0, CX + MOVD X1, R8 + MOVD X2, R9 + MOVD X3, AX + PSHUFL $0x39, X0, X0 + PSHUFL $0x39, X1, X1 + PSHUFL $0x39, X2, X2 + PSHUFL $0x39, X3, X3 + XORL 20(SI), CX + XORL 4(SI), R8 + XORL 52(SI), R9 + XORL 36(SI), AX + MOVL CX, 20(DI) + MOVL R8, 4(DI) + MOVL R9, 52(DI) + MOVL AX, 36(DI) + MOVD X0, CX + MOVD X1, R8 + MOVD X2, R9 + MOVD X3, AX + PSHUFL $0x39, X0, X0 + PSHUFL $0x39, X1, X1 + PSHUFL $0x39, X2, X2 + PSHUFL $0x39, X3, X3 + XORL 40(SI), CX + XORL 24(SI), R8 + XORL 8(SI), R9 + XORL 56(SI), AX + MOVL CX, 40(DI) + MOVL R8, 24(DI) + MOVL R9, 8(DI) + MOVL AX, 56(DI) + MOVD X0, CX + MOVD X1, R8 + MOVD X2, R9 + MOVD X3, AX + XORL 60(SI), CX + XORL 44(SI), R8 + XORL 28(SI), R9 + XORL 12(SI), AX + MOVL CX, 60(DI) + MOVL R8, 44(DI) + MOVL R9, 28(DI) + MOVL AX, 12(DI) + MOVQ 352(R12), R9 + MOVL 16(R12), CX + MOVL 36(R12), R8 + ADDQ $0x01, CX + SHLQ $0x20, R8 + ADDQ R8, CX + MOVQ CX, R8 + SHRQ $0x20, R8 + MOVL CX, 16(R12) + MOVL R8, 36(R12) + CMPQ R9, $0x40 + JA BYTESATLEAST65 + JAE BYTESATLEAST64 + MOVQ DI, SI + MOVQ DX, DI + MOVQ R9, CX REP; MOVSB - BYTESATLEAST64: - DONE: + +BYTESATLEAST64: +DONE: RET - BYTESATLEAST65: - SUBQ $64,R9 - ADDQ $64,DI - ADDQ $64,SI - JMP BYTESBETWEEN1AND255 + +BYTESATLEAST65: + SUBQ $0x40, R9 + ADDQ $0x40, DI + ADDQ $0x40, SI + JMP BYTESBETWEEN1AND255 diff --git a/vendor/golang.org/x/net/LICENSE b/vendor/golang.org/x/net/LICENSE index 6a66aea..2a7cf70 100644 --- a/vendor/golang.org/x/net/LICENSE +++ b/vendor/golang.org/x/net/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2009 The Go Authors. All rights reserved. +Copyright 2009 The Go Authors. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -10,7 +10,7 @@ notice, this list of conditions and the following disclaimer. copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * Neither the name of Google Inc. nor the names of its + * Neither the name of Google LLC nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. diff --git a/vendor/golang.org/x/net/html/doc.go b/vendor/golang.org/x/net/html/doc.go index 3a7e5ab..885c4c5 100644 --- a/vendor/golang.org/x/net/html/doc.go +++ b/vendor/golang.org/x/net/html/doc.go @@ -78,16 +78,11 @@ example, to process each anchor node in depth-first order: if err != nil { // ... } - var f func(*html.Node) - f = func(n *html.Node) { + for n := range doc.Descendants() { if n.Type == html.ElementNode && n.Data == "a" { // Do something with n... } - for c := n.FirstChild; c != nil; c = c.NextSibling { - f(c) - } } - f(doc) The relevant specifications include: https://html.spec.whatwg.org/multipage/syntax.html and diff --git a/vendor/golang.org/x/net/html/doctype.go b/vendor/golang.org/x/net/html/doctype.go index c484e5a..bca3ae9 100644 --- a/vendor/golang.org/x/net/html/doctype.go +++ b/vendor/golang.org/x/net/html/doctype.go @@ -87,7 +87,7 @@ func parseDoctype(s string) (n *Node, quirks bool) { } } if lastAttr := n.Attr[len(n.Attr)-1]; lastAttr.Key == "system" && - strings.ToLower(lastAttr.Val) == "http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd" { + strings.EqualFold(lastAttr.Val, "http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd") { quirks = true } } diff --git a/vendor/golang.org/x/net/html/foreign.go b/vendor/golang.org/x/net/html/foreign.go index 9da9e9d..e8515d8 100644 --- a/vendor/golang.org/x/net/html/foreign.go +++ b/vendor/golang.org/x/net/html/foreign.go @@ -40,8 +40,7 @@ func htmlIntegrationPoint(n *Node) bool { if n.Data == "annotation-xml" { for _, a := range n.Attr { if a.Key == "encoding" { - val := strings.ToLower(a.Val) - if val == "text/html" || val == "application/xhtml+xml" { + if strings.EqualFold(a.Val, "text/html") || strings.EqualFold(a.Val, "application/xhtml+xml") { return true } } diff --git a/vendor/golang.org/x/net/html/iter.go b/vendor/golang.org/x/net/html/iter.go new file mode 100644 index 0000000..54be8fd --- /dev/null +++ b/vendor/golang.org/x/net/html/iter.go @@ -0,0 +1,56 @@ +// Copyright 2024 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build go1.23 + +package html + +import "iter" + +// Ancestors returns an iterator over the ancestors of n, starting with n.Parent. +// +// Mutating a Node or its parents while iterating may have unexpected results. +func (n *Node) Ancestors() iter.Seq[*Node] { + _ = n.Parent // eager nil check + + return func(yield func(*Node) bool) { + for p := n.Parent; p != nil && yield(p); p = p.Parent { + } + } +} + +// ChildNodes returns an iterator over the immediate children of n, +// starting with n.FirstChild. +// +// Mutating a Node or its children while iterating may have unexpected results. +func (n *Node) ChildNodes() iter.Seq[*Node] { + _ = n.FirstChild // eager nil check + + return func(yield func(*Node) bool) { + for c := n.FirstChild; c != nil && yield(c); c = c.NextSibling { + } + } + +} + +// Descendants returns an iterator over all nodes recursively beneath +// n, excluding n itself. Nodes are visited in depth-first preorder. +// +// Mutating a Node or its descendants while iterating may have unexpected results. +func (n *Node) Descendants() iter.Seq[*Node] { + _ = n.FirstChild // eager nil check + + return func(yield func(*Node) bool) { + n.descendants(yield) + } +} + +func (n *Node) descendants(yield func(*Node) bool) bool { + for c := range n.ChildNodes() { + if !yield(c) || !c.descendants(yield) { + return false + } + } + return true +} diff --git a/vendor/golang.org/x/net/html/node.go b/vendor/golang.org/x/net/html/node.go index 1350eef..77741a1 100644 --- a/vendor/golang.org/x/net/html/node.go +++ b/vendor/golang.org/x/net/html/node.go @@ -38,6 +38,10 @@ var scopeMarker = Node{Type: scopeMarkerNode} // that it looks like "a(SB),NOSPLIT,$0-0 + JMP libc_sysctl(SB) +GLOBL ·libc_sysctl_trampoline_addr(SB), RODATA, $8 +DATA ·libc_sysctl_trampoline_addr(SB)/8, $libc_sysctl_trampoline<>(SB) + +TEXT libc_sysctlbyname_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_sysctlbyname(SB) +GLOBL ·libc_sysctlbyname_trampoline_addr(SB), RODATA, $8 +DATA ·libc_sysctlbyname_trampoline_addr(SB)/8, $libc_sysctlbyname_trampoline<>(SB) diff --git a/vendor/golang.org/x/sys/cpu/cpu.go b/vendor/golang.org/x/sys/cpu/cpu.go index 8fa707a..02609d5 100644 --- a/vendor/golang.org/x/sys/cpu/cpu.go +++ b/vendor/golang.org/x/sys/cpu/cpu.go @@ -105,6 +105,8 @@ var ARM64 struct { HasSVE bool // Scalable Vector Extensions HasSVE2 bool // Scalable Vector Extensions 2 HasASIMDFHM bool // Advanced SIMD multiplication FP16 to FP32 + HasDIT bool // Data Independent Timing support + HasI8MM bool // Advanced SIMD Int8 matrix multiplication instructions _ CacheLinePad } @@ -199,6 +201,25 @@ var S390X struct { _ CacheLinePad } +// RISCV64 contains the supported CPU features and performance characteristics for riscv64 +// platforms. The booleans in RISCV64, with the exception of HasFastMisaligned, indicate +// the presence of RISC-V extensions. +// +// It is safe to assume that all the RV64G extensions are supported and so they are omitted from +// this structure. As riscv64 Go programs require at least RV64G, the code that populates +// this structure cannot run successfully if some of the RV64G extensions are missing. +// The struct is padded to avoid false sharing. +var RISCV64 struct { + _ CacheLinePad + HasFastMisaligned bool // Fast misaligned accesses + HasC bool // Compressed instruction-set extension + HasV bool // Vector extension compatible with RVV 1.0 + HasZba bool // Address generation instructions extension + HasZbb bool // Basic bit-manipulation extension + HasZbs bool // Single-bit instructions extension + _ CacheLinePad +} + func init() { archInit() initOptions() diff --git a/vendor/golang.org/x/sys/cpu/cpu_arm64.go b/vendor/golang.org/x/sys/cpu/cpu_arm64.go index 0e27a21..af2aa99 100644 --- a/vendor/golang.org/x/sys/cpu/cpu_arm64.go +++ b/vendor/golang.org/x/sys/cpu/cpu_arm64.go @@ -38,6 +38,8 @@ func initOptions() { {Name: "dcpop", Feature: &ARM64.HasDCPOP}, {Name: "asimddp", Feature: &ARM64.HasASIMDDP}, {Name: "asimdfhm", Feature: &ARM64.HasASIMDFHM}, + {Name: "dit", Feature: &ARM64.HasDIT}, + {Name: "i8mm", Feature: &ARM64.HasI8MM}, } } @@ -145,6 +147,11 @@ func parseARM64SystemRegisters(isar0, isar1, pfr0 uint64) { ARM64.HasLRCPC = true } + switch extractBits(isar1, 52, 55) { + case 1: + ARM64.HasI8MM = true + } + // ID_AA64PFR0_EL1 switch extractBits(pfr0, 16, 19) { case 0: @@ -168,6 +175,11 @@ func parseARM64SystemRegisters(isar0, isar1, pfr0 uint64) { parseARM64SVERegister(getzfr0()) } + + switch extractBits(pfr0, 48, 51) { + case 1: + ARM64.HasDIT = true + } } func parseARM64SVERegister(zfr0 uint64) { diff --git a/vendor/golang.org/x/sys/cpu/cpu_darwin_x86.go b/vendor/golang.org/x/sys/cpu/cpu_darwin_x86.go new file mode 100644 index 0000000..b838cb9 --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_darwin_x86.go @@ -0,0 +1,61 @@ +// Copyright 2024 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build darwin && amd64 && gc + +package cpu + +// darwinSupportsAVX512 checks Darwin kernel for AVX512 support via sysctl +// call (see issue 43089). It also restricts AVX512 support for Darwin to +// kernel version 21.3.0 (MacOS 12.2.0) or later (see issue 49233). +// +// Background: +// Darwin implements a special mechanism to economize on thread state when +// AVX512 specific registers are not in use. This scheme minimizes state when +// preempting threads that haven't yet used any AVX512 instructions, but adds +// special requirements to check for AVX512 hardware support at runtime (e.g. +// via sysctl call or commpage inspection). See issue 43089 and link below for +// full background: +// https://github.com/apple-oss-distributions/xnu/blob/xnu-11215.1.10/osfmk/i386/fpu.c#L214-L240 +// +// Additionally, all versions of the Darwin kernel from 19.6.0 through 21.2.0 +// (corresponding to MacOS 10.15.6 - 12.1) have a bug that can cause corruption +// of the AVX512 mask registers (K0-K7) upon signal return. For this reason +// AVX512 is considered unsafe to use on Darwin for kernel versions prior to +// 21.3.0, where a fix has been confirmed. See issue 49233 for full background. +func darwinSupportsAVX512() bool { + return darwinSysctlEnabled([]byte("hw.optional.avx512f\x00")) && darwinKernelVersionCheck(21, 3, 0) +} + +// Ensure Darwin kernel version is at least major.minor.patch, avoiding dependencies +func darwinKernelVersionCheck(major, minor, patch int) bool { + var release [256]byte + err := darwinOSRelease(&release) + if err != nil { + return false + } + + var mmp [3]int + c := 0 +Loop: + for _, b := range release[:] { + switch { + case b >= '0' && b <= '9': + mmp[c] = 10*mmp[c] + int(b-'0') + case b == '.': + c++ + if c > 2 { + return false + } + case b == 0: + break Loop + default: + return false + } + } + if c != 2 { + return false + } + return mmp[0] > major || mmp[0] == major && (mmp[1] > minor || mmp[1] == minor && mmp[2] >= patch) +} diff --git a/vendor/golang.org/x/sys/cpu/cpu_gc_x86.go b/vendor/golang.org/x/sys/cpu/cpu_gc_x86.go index 910728f..32a4451 100644 --- a/vendor/golang.org/x/sys/cpu/cpu_gc_x86.go +++ b/vendor/golang.org/x/sys/cpu/cpu_gc_x86.go @@ -6,10 +6,10 @@ package cpu -// cpuid is implemented in cpu_x86.s for gc compiler +// cpuid is implemented in cpu_gc_x86.s for gc compiler // and in cpu_gccgo.c for gccgo. func cpuid(eaxArg, ecxArg uint32) (eax, ebx, ecx, edx uint32) -// xgetbv with ecx = 0 is implemented in cpu_x86.s for gc compiler +// xgetbv with ecx = 0 is implemented in cpu_gc_x86.s for gc compiler // and in cpu_gccgo.c for gccgo. func xgetbv() (eax, edx uint32) diff --git a/vendor/golang.org/x/sys/cpu/cpu_x86.s b/vendor/golang.org/x/sys/cpu/cpu_gc_x86.s similarity index 94% rename from vendor/golang.org/x/sys/cpu/cpu_x86.s rename to vendor/golang.org/x/sys/cpu/cpu_gc_x86.s index 7d7ba33..ce208ce 100644 --- a/vendor/golang.org/x/sys/cpu/cpu_x86.s +++ b/vendor/golang.org/x/sys/cpu/cpu_gc_x86.s @@ -18,7 +18,7 @@ TEXT ·cpuid(SB), NOSPLIT, $0-24 RET // func xgetbv() (eax, edx uint32) -TEXT ·xgetbv(SB),NOSPLIT,$0-8 +TEXT ·xgetbv(SB), NOSPLIT, $0-8 MOVL $0, CX XGETBV MOVL AX, eax+0(FP) diff --git a/vendor/golang.org/x/sys/cpu/cpu_gccgo_x86.go b/vendor/golang.org/x/sys/cpu/cpu_gccgo_x86.go index 99c60fe..170d21d 100644 --- a/vendor/golang.org/x/sys/cpu/cpu_gccgo_x86.go +++ b/vendor/golang.org/x/sys/cpu/cpu_gccgo_x86.go @@ -23,9 +23,3 @@ func xgetbv() (eax, edx uint32) { gccgoXgetbv(&a, &d) return a, d } - -// gccgo doesn't build on Darwin, per: -// https://github.com/Homebrew/homebrew-core/blob/HEAD/Formula/gcc.rb#L76 -func darwinSupportsAVX512() bool { - return false -} diff --git a/vendor/golang.org/x/sys/cpu/cpu_linux_arm64.go b/vendor/golang.org/x/sys/cpu/cpu_linux_arm64.go index 3d386d0..f1caf0f 100644 --- a/vendor/golang.org/x/sys/cpu/cpu_linux_arm64.go +++ b/vendor/golang.org/x/sys/cpu/cpu_linux_arm64.go @@ -35,8 +35,10 @@ const ( hwcap_SHA512 = 1 << 21 hwcap_SVE = 1 << 22 hwcap_ASIMDFHM = 1 << 23 + hwcap_DIT = 1 << 24 hwcap2_SVE2 = 1 << 1 + hwcap2_I8MM = 1 << 13 ) // linuxKernelCanEmulateCPUID reports whether we're running @@ -106,9 +108,11 @@ func doinit() { ARM64.HasSHA512 = isSet(hwCap, hwcap_SHA512) ARM64.HasSVE = isSet(hwCap, hwcap_SVE) ARM64.HasASIMDFHM = isSet(hwCap, hwcap_ASIMDFHM) + ARM64.HasDIT = isSet(hwCap, hwcap_DIT) // HWCAP2 feature bits ARM64.HasSVE2 = isSet(hwCap2, hwcap2_SVE2) + ARM64.HasI8MM = isSet(hwCap2, hwcap2_I8MM) } func isSet(hwc uint, value uint) bool { diff --git a/vendor/golang.org/x/sys/cpu/cpu_linux_noinit.go b/vendor/golang.org/x/sys/cpu/cpu_linux_noinit.go index cd63e73..7d902b6 100644 --- a/vendor/golang.org/x/sys/cpu/cpu_linux_noinit.go +++ b/vendor/golang.org/x/sys/cpu/cpu_linux_noinit.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build linux && !arm && !arm64 && !mips64 && !mips64le && !ppc64 && !ppc64le && !s390x +//go:build linux && !arm && !arm64 && !mips64 && !mips64le && !ppc64 && !ppc64le && !s390x && !riscv64 package cpu diff --git a/vendor/golang.org/x/sys/cpu/cpu_linux_riscv64.go b/vendor/golang.org/x/sys/cpu/cpu_linux_riscv64.go new file mode 100644 index 0000000..cb4a0c5 --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_linux_riscv64.go @@ -0,0 +1,137 @@ +// Copyright 2024 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cpu + +import ( + "syscall" + "unsafe" +) + +// RISC-V extension discovery code for Linux. The approach here is to first try the riscv_hwprobe +// syscall falling back to HWCAP to check for the C extension if riscv_hwprobe is not available. +// +// A note on detection of the Vector extension using HWCAP. +// +// Support for the Vector extension version 1.0 was added to the Linux kernel in release 6.5. +// Support for the riscv_hwprobe syscall was added in 6.4. It follows that if the riscv_hwprobe +// syscall is not available then neither is the Vector extension (which needs kernel support). +// The riscv_hwprobe syscall should then be all we need to detect the Vector extension. +// However, some RISC-V board manufacturers ship boards with an older kernel on top of which +// they have back-ported various versions of the Vector extension patches but not the riscv_hwprobe +// patches. These kernels advertise support for the Vector extension using HWCAP. Falling +// back to HWCAP to detect the Vector extension, if riscv_hwprobe is not available, or simply not +// bothering with riscv_hwprobe at all and just using HWCAP may then seem like an attractive option. +// +// Unfortunately, simply checking the 'V' bit in AT_HWCAP will not work as this bit is used by +// RISC-V board and cloud instance providers to mean different things. The Lichee Pi 4A board +// and the Scaleway RV1 cloud instances use the 'V' bit to advertise their support for the unratified +// 0.7.1 version of the Vector Specification. The Banana Pi BPI-F3 and the CanMV-K230 board use +// it to advertise support for 1.0 of the Vector extension. Versions 0.7.1 and 1.0 of the Vector +// extension are binary incompatible. HWCAP can then not be used in isolation to populate the +// HasV field as this field indicates that the underlying CPU is compatible with RVV 1.0. +// +// There is a way at runtime to distinguish between versions 0.7.1 and 1.0 of the Vector +// specification by issuing a RVV 1.0 vsetvli instruction and checking the vill bit of the vtype +// register. This check would allow us to safely detect version 1.0 of the Vector extension +// with HWCAP, if riscv_hwprobe were not available. However, the check cannot +// be added until the assembler supports the Vector instructions. +// +// Note the riscv_hwprobe syscall does not suffer from these ambiguities by design as all of the +// extensions it advertises support for are explicitly versioned. It's also worth noting that +// the riscv_hwprobe syscall is the only way to detect multi-letter RISC-V extensions, e.g., Zba. +// These cannot be detected using HWCAP and so riscv_hwprobe must be used to detect the majority +// of RISC-V extensions. +// +// Please see https://docs.kernel.org/arch/riscv/hwprobe.html for more information. + +// golang.org/x/sys/cpu is not allowed to depend on golang.org/x/sys/unix so we must +// reproduce the constants, types and functions needed to make the riscv_hwprobe syscall +// here. + +const ( + // Copied from golang.org/x/sys/unix/ztypes_linux_riscv64.go. + riscv_HWPROBE_KEY_IMA_EXT_0 = 0x4 + riscv_HWPROBE_IMA_C = 0x2 + riscv_HWPROBE_IMA_V = 0x4 + riscv_HWPROBE_EXT_ZBA = 0x8 + riscv_HWPROBE_EXT_ZBB = 0x10 + riscv_HWPROBE_EXT_ZBS = 0x20 + riscv_HWPROBE_KEY_CPUPERF_0 = 0x5 + riscv_HWPROBE_MISALIGNED_FAST = 0x3 + riscv_HWPROBE_MISALIGNED_MASK = 0x7 +) + +const ( + // sys_RISCV_HWPROBE is copied from golang.org/x/sys/unix/zsysnum_linux_riscv64.go. + sys_RISCV_HWPROBE = 258 +) + +// riscvHWProbePairs is copied from golang.org/x/sys/unix/ztypes_linux_riscv64.go. +type riscvHWProbePairs struct { + key int64 + value uint64 +} + +const ( + // CPU features + hwcap_RISCV_ISA_C = 1 << ('C' - 'A') +) + +func doinit() { + // A slice of key/value pair structures is passed to the RISCVHWProbe syscall. The key + // field should be initialised with one of the key constants defined above, e.g., + // RISCV_HWPROBE_KEY_IMA_EXT_0. The syscall will set the value field to the appropriate value. + // If the kernel does not recognise a key it will set the key field to -1 and the value field to 0. + + pairs := []riscvHWProbePairs{ + {riscv_HWPROBE_KEY_IMA_EXT_0, 0}, + {riscv_HWPROBE_KEY_CPUPERF_0, 0}, + } + + // This call only indicates that extensions are supported if they are implemented on all cores. + if riscvHWProbe(pairs, 0) { + if pairs[0].key != -1 { + v := uint(pairs[0].value) + RISCV64.HasC = isSet(v, riscv_HWPROBE_IMA_C) + RISCV64.HasV = isSet(v, riscv_HWPROBE_IMA_V) + RISCV64.HasZba = isSet(v, riscv_HWPROBE_EXT_ZBA) + RISCV64.HasZbb = isSet(v, riscv_HWPROBE_EXT_ZBB) + RISCV64.HasZbs = isSet(v, riscv_HWPROBE_EXT_ZBS) + } + if pairs[1].key != -1 { + v := pairs[1].value & riscv_HWPROBE_MISALIGNED_MASK + RISCV64.HasFastMisaligned = v == riscv_HWPROBE_MISALIGNED_FAST + } + } + + // Let's double check with HWCAP if the C extension does not appear to be supported. + // This may happen if we're running on a kernel older than 6.4. + + if !RISCV64.HasC { + RISCV64.HasC = isSet(hwCap, hwcap_RISCV_ISA_C) + } +} + +func isSet(hwc uint, value uint) bool { + return hwc&value != 0 +} + +// riscvHWProbe is a simplified version of the generated wrapper function found in +// golang.org/x/sys/unix/zsyscall_linux_riscv64.go. We simplify it by removing the +// cpuCount and cpus parameters which we do not need. We always want to pass 0 for +// these parameters here so the kernel only reports the extensions that are present +// on all cores. +func riscvHWProbe(pairs []riscvHWProbePairs, flags uint) bool { + var _zero uintptr + var p0 unsafe.Pointer + if len(pairs) > 0 { + p0 = unsafe.Pointer(&pairs[0]) + } else { + p0 = unsafe.Pointer(&_zero) + } + + _, _, e1 := syscall.Syscall6(sys_RISCV_HWPROBE, uintptr(p0), uintptr(len(pairs)), uintptr(0), uintptr(0), uintptr(flags), 0) + return e1 == 0 +} diff --git a/vendor/golang.org/x/sys/cpu/cpu_other_x86.go b/vendor/golang.org/x/sys/cpu/cpu_other_x86.go new file mode 100644 index 0000000..a0fd7e2 --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_other_x86.go @@ -0,0 +1,11 @@ +// Copyright 2024 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build 386 || amd64p32 || (amd64 && (!darwin || !gc)) + +package cpu + +func darwinSupportsAVX512() bool { + panic("only implemented for gc && amd64 && darwin") +} diff --git a/vendor/golang.org/x/sys/cpu/cpu_riscv64.go b/vendor/golang.org/x/sys/cpu/cpu_riscv64.go index 7f0c79c..aca3199 100644 --- a/vendor/golang.org/x/sys/cpu/cpu_riscv64.go +++ b/vendor/golang.org/x/sys/cpu/cpu_riscv64.go @@ -8,4 +8,13 @@ package cpu const cacheLineSize = 64 -func initOptions() {} +func initOptions() { + options = []option{ + {Name: "fastmisaligned", Feature: &RISCV64.HasFastMisaligned}, + {Name: "c", Feature: &RISCV64.HasC}, + {Name: "v", Feature: &RISCV64.HasV}, + {Name: "zba", Feature: &RISCV64.HasZba}, + {Name: "zbb", Feature: &RISCV64.HasZbb}, + {Name: "zbs", Feature: &RISCV64.HasZbs}, + } +} diff --git a/vendor/golang.org/x/sys/cpu/cpu_x86.go b/vendor/golang.org/x/sys/cpu/cpu_x86.go index c29f5e4..600a680 100644 --- a/vendor/golang.org/x/sys/cpu/cpu_x86.go +++ b/vendor/golang.org/x/sys/cpu/cpu_x86.go @@ -92,10 +92,8 @@ func archInit() { osSupportsAVX = isSet(1, eax) && isSet(2, eax) if runtime.GOOS == "darwin" { - // Darwin doesn't save/restore AVX-512 mask registers correctly across signal handlers. - // Since users can't rely on mask register contents, let's not advertise AVX-512 support. - // See issue 49233. - osSupportsAVX512 = false + // Darwin requires special AVX512 checks, see cpu_darwin_x86.go + osSupportsAVX512 = osSupportsAVX && darwinSupportsAVX512() } else { // Check if OPMASK and ZMM registers have OS support. osSupportsAVX512 = osSupportsAVX && isSet(5, eax) && isSet(6, eax) && isSet(7, eax) diff --git a/vendor/golang.org/x/sys/cpu/syscall_darwin_x86_gc.go b/vendor/golang.org/x/sys/cpu/syscall_darwin_x86_gc.go new file mode 100644 index 0000000..4d0888b --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/syscall_darwin_x86_gc.go @@ -0,0 +1,98 @@ +// Copyright 2024 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Minimal copy of x/sys/unix so the cpu package can make a +// system call on Darwin without depending on x/sys/unix. + +//go:build darwin && amd64 && gc + +package cpu + +import ( + "syscall" + "unsafe" +) + +type _C_int int32 + +// adapted from unix.Uname() at x/sys/unix/syscall_darwin.go L419 +func darwinOSRelease(release *[256]byte) error { + // from x/sys/unix/zerrors_openbsd_amd64.go + const ( + CTL_KERN = 0x1 + KERN_OSRELEASE = 0x2 + ) + + mib := []_C_int{CTL_KERN, KERN_OSRELEASE} + n := unsafe.Sizeof(*release) + + return sysctl(mib, &release[0], &n, nil, 0) +} + +type Errno = syscall.Errno + +var _zero uintptr // Single-word zero for use when we need a valid pointer to 0 bytes. + +// from x/sys/unix/zsyscall_darwin_amd64.go L791-807 +func sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) error { + var _p0 unsafe.Pointer + if len(mib) > 0 { + _p0 = unsafe.Pointer(&mib[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + if _, _, err := syscall_syscall6( + libc_sysctl_trampoline_addr, + uintptr(_p0), + uintptr(len(mib)), + uintptr(unsafe.Pointer(old)), + uintptr(unsafe.Pointer(oldlen)), + uintptr(unsafe.Pointer(new)), + uintptr(newlen), + ); err != 0 { + return err + } + + return nil +} + +var libc_sysctl_trampoline_addr uintptr + +// adapted from internal/cpu/cpu_arm64_darwin.go +func darwinSysctlEnabled(name []byte) bool { + out := int32(0) + nout := unsafe.Sizeof(out) + if ret := sysctlbyname(&name[0], (*byte)(unsafe.Pointer(&out)), &nout, nil, 0); ret != nil { + return false + } + return out > 0 +} + +//go:cgo_import_dynamic libc_sysctl sysctl "/usr/lib/libSystem.B.dylib" + +var libc_sysctlbyname_trampoline_addr uintptr + +// adapted from runtime/sys_darwin.go in the pattern of sysctl() above, as defined in x/sys/unix +func sysctlbyname(name *byte, old *byte, oldlen *uintptr, new *byte, newlen uintptr) error { + if _, _, err := syscall_syscall6( + libc_sysctlbyname_trampoline_addr, + uintptr(unsafe.Pointer(name)), + uintptr(unsafe.Pointer(old)), + uintptr(unsafe.Pointer(oldlen)), + uintptr(unsafe.Pointer(new)), + uintptr(newlen), + 0, + ); err != 0 { + return err + } + + return nil +} + +//go:cgo_import_dynamic libc_sysctlbyname sysctlbyname "/usr/lib/libSystem.B.dylib" + +// Implemented in the runtime package (runtime/sys_darwin.go) +func syscall_syscall6(fn, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno) + +//go:linkname syscall_syscall6 syscall.syscall6 diff --git a/vendor/golang.org/x/text/LICENSE b/vendor/golang.org/x/text/LICENSE index 6a66aea..2a7cf70 100644 --- a/vendor/golang.org/x/text/LICENSE +++ b/vendor/golang.org/x/text/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2009 The Go Authors. All rights reserved. +Copyright 2009 The Go Authors. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -10,7 +10,7 @@ notice, this list of conditions and the following disclaimer. copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * Neither the name of Google Inc. nor the names of its + * Neither the name of Google LLC nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. diff --git a/vendor/modules.txt b/vendor/modules.txt index 1c00170..97a39c7 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1,19 +1,89 @@ +# github.com/a-h/templ v0.2.747 +## explicit; go 1.21 +github.com/a-h/templ +github.com/a-h/templ/safehtml # github.com/anaskhan96/soup v1.2.5 ## explicit; go 1.13 github.com/anaskhan96/soup # github.com/bwmarrin/discordgo v0.28.1 ## explicit; go 1.13 github.com/bwmarrin/discordgo -# github.com/darklab8/go-typelog v0.6.1 +# github.com/darklab8/fl-darkstat v1.64.3 +## explicit; go 1.23.0 +github.com/darklab8/fl-darkstat/configs/cfg +github.com/darklab8/fl-darkstat/configs/configs_mapped +github.com/darklab8/fl-darkstat/configs/configs_mapped/flsr/flsr_recipes +github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/const_mapped +github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/equipment_mapped +github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/equipment_mapped/equip_mapped +github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/equipment_mapped/market_mapped +github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/equipment_mapped/weaponmoddb +github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/initialworld +github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/initialworld/flhash +github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/interface_mapped +github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/missions_mapped/empathy_mapped +github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/missions_mapped/faction_props_mapped +github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/missions_mapped/mbases_mapped +github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/missions_mapped/npc_ships +github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/rnd_msns_mapped/diff2money +github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/rnd_msns_mapped/npcranktodiff +github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/ship_mapped +github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/solar_mapped/loadouts_mapped +github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/solar_mapped/solararch_mapped +github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/universe_mapped +github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/data_mapped/universe_mapped/systems_mapped +github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/exe_mapped +github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/exe_mapped/go-binary-pack +github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/infocard_mapped +github.com/darklab8/fl-darkstat/configs/configs_mapped/freelancer_mapped/infocard_mapped/infocard +github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/bin +github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/bini +github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/filefind +github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/filefind/file +github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/iniload +github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/inireader +github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/inireader/inireader_types +github.com/darklab8/fl-darkstat/configs/configs_mapped/parserutils/semantic +github.com/darklab8/fl-darkstat/configs/configs_settings +github.com/darklab8/fl-darkstat/configs/configs_settings/logus +github.com/darklab8/fl-darkstat/configs/discovery/base_recipe_items +github.com/darklab8/fl-darkstat/configs/discovery/discoprices +github.com/darklab8/fl-darkstat/configs/discovery/playercntl_rephacks +github.com/darklab8/fl-darkstat/configs/discovery/pob_goods +github.com/darklab8/fl-darkstat/configs/discovery/techcompat +github.com/darklab8/fl-darkstat/configs/overrides +github.com/darklab8/fl-darkstat/configs/tests +github.com/darklab8/fl-darkstat/darkcore/builder +github.com/darklab8/fl-darkstat/darkcore/core_front +github.com/darklab8/fl-darkstat/darkcore/core_static +github.com/darklab8/fl-darkstat/darkcore/core_types +github.com/darklab8/fl-darkstat/darkcore/settings +github.com/darklab8/fl-darkstat/darkcore/settings/logus +github.com/darklab8/fl-darkstat/darkrpc +github.com/darklab8/fl-darkstat/darkstat/appdata +github.com/darklab8/fl-darkstat/darkstat/configs_export +github.com/darklab8/fl-darkstat/darkstat/configs_export/trades +github.com/darklab8/fl-darkstat/darkstat/front/static +github.com/darklab8/fl-darkstat/darkstat/front/static_front +github.com/darklab8/fl-darkstat/darkstat/front/tab +github.com/darklab8/fl-darkstat/darkstat/front/types +github.com/darklab8/fl-darkstat/darkstat/front/urls +github.com/darklab8/fl-darkstat/darkstat/settings +github.com/darklab8/fl-darkstat/darkstat/settings/logus +# github.com/darklab8/fl-data-discovery v0.4.0 ## explicit; go 1.21.1 +github.com/darklab8/fl-data-discovery/autopatcher +# github.com/darklab8/go-typelog v0.6.2 +## explicit; go 1.22.5 github.com/darklab8/go-typelog/examples/logus github.com/darklab8/go-typelog/typelog -# github.com/darklab8/go-utils v0.21.0 +# github.com/darklab8/go-utils v0.21.1 ## explicit; go 1.22.5 github.com/darklab8/go-utils/utils github.com/darklab8/go-utils/utils/enverant github.com/darklab8/go-utils/utils/ptr github.com/darklab8/go-utils/utils/timeit +github.com/darklab8/go-utils/utils/utils_filepath github.com/darklab8/go-utils/utils/utils_logus github.com/darklab8/go-utils/utils/utils_os github.com/darklab8/go-utils/utils/utils_settings @@ -59,24 +129,25 @@ github.com/spf13/cobra # github.com/spf13/pflag v1.0.5 ## explicit; go 1.12 github.com/spf13/pflag -# github.com/stretchr/testify v1.9.0 +# github.com/stretchr/testify v1.10.0 ## explicit; go 1.17 github.com/stretchr/testify/assert -# golang.org/x/crypto v0.25.0 +github.com/stretchr/testify/assert/yaml +# golang.org/x/crypto v0.32.0 ## explicit; go 1.20 golang.org/x/crypto/internal/alias golang.org/x/crypto/internal/poly1305 golang.org/x/crypto/nacl/secretbox golang.org/x/crypto/salsa20/salsa -# golang.org/x/net v0.27.0 +# golang.org/x/net v0.34.0 ## explicit; go 1.18 golang.org/x/net/html golang.org/x/net/html/atom golang.org/x/net/html/charset -# golang.org/x/sys v0.22.0 +# golang.org/x/sys v0.29.0 ## explicit; go 1.18 golang.org/x/sys/cpu -# golang.org/x/text v0.16.0 +# golang.org/x/text v0.21.0 ## explicit; go 1.18 golang.org/x/text/cases golang.org/x/text/encoding