mirror of
https://github.com/onyx-and-iris/q3rcon.git
synced 2025-04-11 08:33:49 +01:00
Compare commits
No commits in common. "main" and "v0.0.2" have entirely different histories.
31
.github/workflows/release.yml
vendored
31
.github/workflows/release.yml
vendored
@ -1,31 +0,0 @@
|
|||||||
name: goreleaser
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
tags:
|
|
||||||
- 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
goreleaser:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
-
|
|
||||||
name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
-
|
|
||||||
name: Set up Go
|
|
||||||
uses: actions/setup-go@v5
|
|
||||||
-
|
|
||||||
name: Run GoReleaser
|
|
||||||
uses: goreleaser/goreleaser-action@v6
|
|
||||||
with:
|
|
||||||
distribution: goreleaser
|
|
||||||
version: '~> v2'
|
|
||||||
args: release --clean
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
30
.github/workflows/update-go-modules.yml
vendored
30
.github/workflows/update-go-modules.yml
vendored
@ -1,30 +0,0 @@
|
|||||||
name: Auto-Update Go Modules
|
|
||||||
|
|
||||||
on:
|
|
||||||
schedule:
|
|
||||||
- cron: "0 0 * * 1" # Runs every Monday at midnight
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
update-go-modules:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout Code
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Set up Go
|
|
||||||
uses: actions/setup-go@v5
|
|
||||||
with:
|
|
||||||
go-version: stable
|
|
||||||
|
|
||||||
- name: Update Dependencies
|
|
||||||
run: |
|
|
||||||
go get -u ./...
|
|
||||||
go mod tidy
|
|
||||||
git config user.name "github-actions[bot]"
|
|
||||||
git config user.email "github-actions[bot]@users.noreply.github.com"
|
|
||||||
git add go.mod go.sum
|
|
||||||
git commit -m "chore: auto-update Go modules"
|
|
||||||
git push
|
|
5
.gitignore
vendored
5
.gitignore
vendored
@ -25,7 +25,4 @@ go.work.sum
|
|||||||
# env file
|
# env file
|
||||||
.env
|
.env
|
||||||
|
|
||||||
# Added by goreleaser init:
|
cmd/aeiou
|
||||||
dist/
|
|
||||||
|
|
||||||
cmd/codrcon
|
|
@ -1,55 +0,0 @@
|
|||||||
# This is an example .goreleaser.yml file with some sensible defaults.
|
|
||||||
# Make sure to check the documentation at https://goreleaser.com
|
|
||||||
|
|
||||||
# The lines below are called `modelines`. See `:help modeline`
|
|
||||||
# Feel free to remove those if you don't want/need to use them.
|
|
||||||
# yaml-language-server: $schema=https://goreleaser.com/static/schema.json
|
|
||||||
# vim: set ts=2 sw=2 tw=0 fo=cnqoj
|
|
||||||
|
|
||||||
version: 2
|
|
||||||
|
|
||||||
before:
|
|
||||||
hooks:
|
|
||||||
# You may remove this if you don't use go modules.
|
|
||||||
- go mod tidy
|
|
||||||
# you may remove this if you don't need go generate
|
|
||||||
- go generate ./...
|
|
||||||
|
|
||||||
builds:
|
|
||||||
- main: ./cmd/q3rcon/
|
|
||||||
env:
|
|
||||||
- CGO_ENABLED=0
|
|
||||||
goos:
|
|
||||||
- linux
|
|
||||||
- windows
|
|
||||||
goarch:
|
|
||||||
- amd64
|
|
||||||
|
|
||||||
archives:
|
|
||||||
- formats: ['tar.gz']
|
|
||||||
# this name template makes the OS and Arch compatible with the results of `uname`.
|
|
||||||
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 }}
|
|
||||||
# use zip for windows archives
|
|
||||||
format_overrides:
|
|
||||||
- goos: windows
|
|
||||||
formats: ['zip']
|
|
||||||
|
|
||||||
changelog:
|
|
||||||
sort: asc
|
|
||||||
filters:
|
|
||||||
exclude:
|
|
||||||
- '^docs:'
|
|
||||||
- '^test:'
|
|
||||||
|
|
||||||
release:
|
|
||||||
footer: >-
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
Released by [GoReleaser](https://github.com/goreleaser/goreleaser).
|
|
32
CHANGELOG.md
32
CHANGELOG.md
@ -11,39 +11,9 @@ Before any major/minor/patch bump all unit tests will be run to verify they pass
|
|||||||
|
|
||||||
- [x]
|
- [x]
|
||||||
|
|
||||||
# [0.3.0] - 2025-04-05
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- `-loglevel` flag is now of type string. It accepts any one of trace, debug, info, warn, error, fatal or panic.
|
|
||||||
- It defaults to warn.
|
|
||||||
|
|
||||||
# [0.2.0] - 2025-02-03
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- If neither the interactive flag is passed or any command line arguments then a *rcon status* command will be run.
|
|
||||||
|
|
||||||
# [0.1.0] - 2024-11-29
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- `-P` flag changed to `-r` for setting rcon password. This is to disambiguate it from the port (-p) flag.
|
|
||||||
|
|
||||||
# [0.0.3] - 2024-11-24
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- {Rcon}.login is no longer exported since it's called internally by the constructor.
|
|
||||||
- When checking the timeouts map the cmd is split from its arguments. This allows setting a timeout value for all `map mp_` for example.
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- Timeout values for commands in the timeouts map are now logged at Debug level.
|
|
||||||
|
|
||||||
# [0.0.1] - 2024-11-04
|
# [0.0.1] - 2024-11-04
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Initial release, package implements Rcon using the Q3 protocol.
|
- Initial release, package implements Rcon using the Q3 procotocl.
|
||||||
- A basic CLI implementation accepting configuration flags.
|
- A basic CLI implementation accepting configuration flags.
|
||||||
|
21
LICENSE
21
LICENSE
@ -1,21 +0,0 @@
|
|||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2024 Onyx and Iris
|
|
||||||
|
|
||||||
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.
|
|
36
README.md
36
README.md
@ -93,15 +93,15 @@ rcon, err := q3rcon.New(
|
|||||||
|
|
||||||
## Command line
|
## Command line
|
||||||
|
|
||||||
Pass `host`, `port` and `rconpass` as flags, for example:
|
Pass `host`, `port` and `password` as flags, for example:
|
||||||
|
|
||||||
```
|
```
|
||||||
q3rcon -h=localhost -p=30000 -r="rconpassword" "mapname"
|
q3rcon -h=localhost -p=30000 -P="rconpassword" "mapname"
|
||||||
```
|
```
|
||||||
|
|
||||||
- `host` defaults to "localhost"
|
- `host` defaults to "localhost"
|
||||||
- `port` defaults to 28960
|
- `port` defaults to 28960
|
||||||
- `rconpass` defaults to ""
|
- `password` defaults to ""
|
||||||
|
|
||||||
Arguments following the flags will be sent as rcon commands. You may send multiple arguments.
|
Arguments following the flags will be sent as rcon commands. You may send multiple arguments.
|
||||||
|
|
||||||
@ -109,8 +109,8 @@ Arguments following the flags will be sent as rcon commands. You may send multip
|
|||||||
|
|
||||||
Pass `interactive (-i shorthand)` flag to enable interactive mode, for example:
|
Pass `interactive (-i shorthand)` flag to enable interactive mode, for example:
|
||||||
|
|
||||||
```bash
|
```
|
||||||
q3rcon -h=localhost -p=30000 -r="rconpassword" -i
|
q3rcon -h=localhost -p=30000 -P="rconpassword" -i
|
||||||
```
|
```
|
||||||
|
|
||||||
If interactive mode is enabled, any arguments sent on the command line will be ignored.
|
If interactive mode is enabled, any arguments sent on the command line will be ignored.
|
||||||
@ -123,31 +123,9 @@ Since you can include the q3rcon package into your own package you can easily ma
|
|||||||
|
|
||||||
## Logging
|
## Logging
|
||||||
|
|
||||||
The `-loglevel` flag allows you to control the verbosity of the application's logging output.
|
Log level may be set by passing the `-l` flag with a number from 0 up to 6 where
|
||||||
|
|
||||||
Acceptable values for this flag are:
|
|
||||||
|
|
||||||
- `trace`
|
|
||||||
- `debug`
|
|
||||||
- `info`
|
|
||||||
- `warn`
|
|
||||||
- `error`
|
|
||||||
- `fatal`
|
|
||||||
- `panic`
|
|
||||||
|
|
||||||
For example, to set the log level to `debug`, you can use:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
q3rcon -p=28960 -r="rconpassword" -loglevel=debug -i
|
|
||||||
```
|
|
||||||
|
|
||||||
The default log level is `warn` if the flag is not specified.
|
|
||||||
|
|
||||||
## Further Notes
|
|
||||||
|
|
||||||
This rcon client is fully compatible with the [Q3 Rcon Proxy][q3rcon-proxy] package.
|
|
||||||
|
|
||||||
|
0 = Panic, 1 = Fatal, 2 = Error, 3 = Warning, 4 = Info, 5 = Debug, 6 = Trace
|
||||||
|
|
||||||
[status]: ./img/status.png
|
[status]: ./img/status.png
|
||||||
[mapname]: ./img/mapname.png
|
[mapname]: ./img/mapname.png
|
||||||
[q3rcon-proxy]: https://github.com/onyx-and-iris/q3rcon-proxy/tree/dev
|
|
55
Taskfile.yml
55
Taskfile.yml
@ -1,55 +0,0 @@
|
|||||||
version: '3'
|
|
||||||
|
|
||||||
vars:
|
|
||||||
PROGRAM: q3rcon
|
|
||||||
SHELL: '{{if eq .OS "Windows_NT"}}powershell{{end}}'
|
|
||||||
BIN_DIR: bin
|
|
||||||
|
|
||||||
WINDOWS: '{{.BIN_DIR}}/{{.PROGRAM}}_windows_amd64.exe'
|
|
||||||
LINUX: '{{.BIN_DIR}}/{{.PROGRAM}}_linux_amd64'
|
|
||||||
GIT_COMMIT:
|
|
||||||
sh: git log -n 1 --format=%h
|
|
||||||
|
|
||||||
tasks:
|
|
||||||
default:
|
|
||||||
desc: Build the q3rcon project
|
|
||||||
cmds:
|
|
||||||
- task: build
|
|
||||||
|
|
||||||
build:
|
|
||||||
desc: Build the q3rcon project
|
|
||||||
deps: [vet]
|
|
||||||
cmds:
|
|
||||||
- task: build-windows
|
|
||||||
- task: build-linux
|
|
||||||
|
|
||||||
vet:
|
|
||||||
desc: Vet the code
|
|
||||||
deps: [fmt]
|
|
||||||
cmds:
|
|
||||||
- go vet ./...
|
|
||||||
|
|
||||||
fmt:
|
|
||||||
desc: Fmt the code
|
|
||||||
cmds:
|
|
||||||
- go fmt ./...
|
|
||||||
|
|
||||||
build-windows:
|
|
||||||
desc: Build the q3rcon project for Windows
|
|
||||||
cmds:
|
|
||||||
- GOOS=windows GOARCH=amd64 go build -o {{.WINDOWS}} -ldflags="-X main.Version={{.GIT_COMMIT}}" ./cmd/{{.PROGRAM}}/
|
|
||||||
|
|
||||||
build-linux:
|
|
||||||
desc: Build the q3rcon project for Linux
|
|
||||||
cmds:
|
|
||||||
- GOOS=linux GOARCH=amd64 go build -o {{.LINUX}} -ldflags="-X main.Version={{.GIT_COMMIT}}" ./cmd/{{.PROGRAM}}/
|
|
||||||
|
|
||||||
test:
|
|
||||||
desc: Run tests
|
|
||||||
cmds:
|
|
||||||
- go test ./...
|
|
||||||
|
|
||||||
clean:
|
|
||||||
desc: Clean the build artifacts
|
|
||||||
cmds:
|
|
||||||
- '{{.SHELL}} rm -r {{.BIN_DIR}}'
|
|
@ -2,10 +2,12 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
|
"errors"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/onyx-and-iris/q3rcon"
|
"github.com/onyx-and-iris/q3rcon"
|
||||||
@ -13,64 +15,67 @@ import (
|
|||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
func exitOnError(err error) {
|
var interactive bool
|
||||||
|
|
||||||
|
func exit(err error) {
|
||||||
_, _ = fmt.Fprintf(os.Stderr, "Error: %s\n", err)
|
_, _ = fmt.Fprintf(os.Stderr, "Error: %s\n", err)
|
||||||
|
flag.Usage()
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
var (
|
var (
|
||||||
host string
|
host string
|
||||||
port int
|
port int
|
||||||
rconpass string
|
password string
|
||||||
interactive bool
|
loglevel int
|
||||||
loglevel string
|
|
||||||
)
|
)
|
||||||
|
|
||||||
flag.StringVar(&host, "host", "localhost", "hostname of the gameserver")
|
flag.StringVar(&host, "host", "localhost", "hostname of the server")
|
||||||
flag.StringVar(&host, "h", "localhost", "hostname of the gameserver (shorthand)")
|
flag.StringVar(&host, "h", "localhost", "hostname of the server (shorthand)")
|
||||||
flag.IntVar(&port, "port", 28960, "port on which the gameserver resides, default is 28960")
|
flag.IntVar(&port, "port", 28960, "port of the server")
|
||||||
flag.IntVar(&port, "p", 28960, "port on which the gameserver resides, default is 28960 (shorthand)")
|
flag.IntVar(&port, "p", 28960, "port of the server (shorthand)")
|
||||||
flag.StringVar(&rconpass, "rconpass", os.Getenv("RCON_PASS"), "rcon password of the gameserver")
|
flag.StringVar(&password, "password", "", "rcon password")
|
||||||
flag.StringVar(&rconpass, "r", os.Getenv("RCON_PASS"), "rcon password of the gameserver (shorthand)")
|
flag.StringVar(&password, "P", "", "rcon password (shorthand)")
|
||||||
|
|
||||||
flag.BoolVar(&interactive, "interactive", false, "run in interactive mode")
|
flag.BoolVar(&interactive, "interactive", false, "run in interactive mode")
|
||||||
flag.BoolVar(&interactive, "i", false, "run in interactive mode")
|
flag.BoolVar(&interactive, "i", false, "run in interactive mode")
|
||||||
|
|
||||||
flag.StringVar(&loglevel, "loglevel", "warn", "log level")
|
flag.IntVar(&loglevel, "loglevel", int(log.WarnLevel), "log level")
|
||||||
flag.StringVar(&loglevel, "l", "warn", "log level (shorthand)")
|
flag.IntVar(&loglevel, "l", int(log.WarnLevel), "log level (shorthand)")
|
||||||
|
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
level, err := log.ParseLevel(loglevel)
|
if slices.Contains(log.AllLevels, log.Level(loglevel)) {
|
||||||
|
log.SetLevel(log.Level(loglevel))
|
||||||
|
}
|
||||||
|
|
||||||
|
rcon, err := connectRcon(host, port, password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
exitOnError(fmt.Errorf("invalid log level: %s", loglevel))
|
log.Fatal(err)
|
||||||
}
|
|
||||||
log.SetLevel(level)
|
|
||||||
|
|
||||||
if port < 1024 || port > 65535 {
|
|
||||||
exitOnError(fmt.Errorf("invalid port value, got: (%d) expected: in range 1024-65535", port))
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(rconpass) < 8 {
|
|
||||||
exitOnError(fmt.Errorf("invalid rcon password, got: (%s) expected: at least 8 characters", rconpass))
|
|
||||||
}
|
|
||||||
|
|
||||||
rcon, err := connectRcon(host, port, rconpass)
|
|
||||||
if err != nil {
|
|
||||||
exitOnError(err)
|
|
||||||
}
|
}
|
||||||
defer rcon.Close()
|
defer rcon.Close()
|
||||||
|
|
||||||
if !interactive {
|
if interactive {
|
||||||
runCommands(flag.Args(), rcon)
|
fmt.Printf("Enter 'Q' to exit.\n>> ")
|
||||||
|
err := interactiveMode(rcon, os.Stdin)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("Enter 'Q' to exit.\n>> ")
|
if len(flag.Args()) == 0 {
|
||||||
err = interactiveMode(rcon, os.Stdin)
|
err = errors.New("no rcon commands passed")
|
||||||
if err != nil {
|
exit(err)
|
||||||
exitOnError(err)
|
}
|
||||||
|
|
||||||
|
for _, arg := range flag.Args() {
|
||||||
|
resp, err := rcon.Send(arg)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fmt.Print(resp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,29 +87,12 @@ func connectRcon(host string, port int, password string) (*q3rcon.Rcon, error) {
|
|||||||
return rcon, nil
|
return rcon, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// runCommands runs the commands given in the flag.Args slice.
|
|
||||||
// If no commands are given, it defaults to running the "status" command.
|
|
||||||
func runCommands(commands []string, rcon *q3rcon.Rcon) {
|
|
||||||
if len(commands) == 0 {
|
|
||||||
commands = append(commands, "status")
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, cmd := range commands {
|
|
||||||
resp, err := rcon.Send(cmd)
|
|
||||||
if err != nil {
|
|
||||||
log.Error(err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
fmt.Print(resp)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// interactiveMode continuously reads from input until a quit signal is given.
|
// interactiveMode continuously reads from input until a quit signal is given.
|
||||||
func interactiveMode(rcon *q3rcon.Rcon, input io.Reader) error {
|
func interactiveMode(rcon *q3rcon.Rcon, input io.Reader) error {
|
||||||
scanner := bufio.NewScanner(input)
|
scanner := bufio.NewScanner(input)
|
||||||
for scanner.Scan() {
|
for scanner.Scan() {
|
||||||
cmd := scanner.Text()
|
cmd := scanner.Text()
|
||||||
if strings.EqualFold(cmd, "Q") {
|
if strings.ToUpper(cmd) == "Q" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,7 +103,6 @@ func interactiveMode(rcon *q3rcon.Rcon, input io.Reader) error {
|
|||||||
}
|
}
|
||||||
fmt.Printf("%s>> ", resp)
|
fmt.Printf("%s>> ", resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
if scanner.Err() != nil {
|
if scanner.Err() != nil {
|
||||||
return scanner.Err()
|
return scanner.Err()
|
||||||
}
|
}
|
||||||
|
11
go.mod
11
go.mod
@ -2,6 +2,13 @@ module github.com/onyx-and-iris/q3rcon
|
|||||||
|
|
||||||
go 1.23.0
|
go 1.23.0
|
||||||
|
|
||||||
require github.com/sirupsen/logrus v1.9.3
|
require (
|
||||||
|
github.com/fatih/color v1.18.0
|
||||||
|
github.com/sirupsen/logrus v1.9.3
|
||||||
|
)
|
||||||
|
|
||||||
require golang.org/x/sys v0.32.0 // indirect
|
require (
|
||||||
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
|
golang.org/x/sys v0.25.0 // indirect
|
||||||
|
)
|
||||||
|
13
go.sum
13
go.sum
@ -1,6 +1,13 @@
|
|||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
|
||||||
|
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
|
||||||
|
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||||
|
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||||
|
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||||
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||||
@ -9,8 +16,10 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
|
|||||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
|
||||||
|
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
@ -3,8 +3,6 @@ package packet
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const bufSz = 512
|
const bufSz = 512
|
||||||
@ -31,6 +29,5 @@ func (r Request) Encode(cmd string) []byte {
|
|||||||
r.buf.Reset()
|
r.buf.Reset()
|
||||||
r.buf.Write(r.Header())
|
r.buf.Write(r.Header())
|
||||||
r.buf.WriteString(fmt.Sprintf(" %s %s", r.password, cmd))
|
r.buf.WriteString(fmt.Sprintf(" %s %s", r.password, cmd))
|
||||||
log.Tracef("Encoded request: %s", r.buf.String())
|
|
||||||
return r.buf.Bytes()
|
return r.buf.Bytes()
|
||||||
}
|
}
|
||||||
|
12
makefile
12
makefile
@ -1,11 +1,11 @@
|
|||||||
PROGRAM = q3rcon
|
program = q3rcon
|
||||||
|
|
||||||
GO = @go
|
GO = @go
|
||||||
BIN_DIR := bin
|
BIN_DIR := bin
|
||||||
|
|
||||||
WINDOWS=$(BIN_DIR)/$(PROGRAM)_windows_amd64.exe
|
WINDOWS=$(BIN_DIR)/$(program)_windows_amd64.exe
|
||||||
LINUX=$(BIN_DIR)/$(PROGRAM)_linux_amd64
|
LINUX=$(BIN_DIR)/$(program)_linux_amd64
|
||||||
VERSION=$(shell git log -n 1 --format=%h)
|
VERSION=$(shell git describe --tags --always --long --dirty)
|
||||||
|
|
||||||
.DEFAULT_GOAL := build
|
.DEFAULT_GOAL := build
|
||||||
|
|
||||||
@ -25,10 +25,10 @@ linux: $(LINUX)
|
|||||||
|
|
||||||
|
|
||||||
$(WINDOWS):
|
$(WINDOWS):
|
||||||
env GOOS=windows GOARCH=amd64 go build -v -o $(WINDOWS) -ldflags="-s -w -X main.version=$(VERSION)" ./cmd/$(PROGRAM)/
|
env GOOS=windows GOARCH=amd64 go build -v -o $(WINDOWS) -ldflags="-s -w -X main.version=$(VERSION)" ./cmd/q3rcon/
|
||||||
|
|
||||||
$(LINUX):
|
$(LINUX):
|
||||||
env GOOS=linux GOARCH=amd64 go build -v -o $(LINUX) -ldflags="-s -w -X main.version=$(VERSION)" ./cmd/$(PROGRAM)/
|
env GOOS=linux GOARCH=amd64 go build -v -o $(LINUX) -ldflags="-s -w -X main.version=$(VERSION)" ./cmd/q3rcon/
|
||||||
|
|
||||||
test:
|
test:
|
||||||
$(GO) test ./...
|
$(GO) test ./...
|
||||||
|
17
q3rcon.go
17
q3rcon.go
@ -7,8 +7,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
|
|
||||||
"github.com/onyx-and-iris/q3rcon/internal/conn"
|
"github.com/onyx-and-iris/q3rcon/internal/conn"
|
||||||
"github.com/onyx-and-iris/q3rcon/internal/packet"
|
"github.com/onyx-and-iris/q3rcon/internal/packet"
|
||||||
)
|
)
|
||||||
@ -49,14 +47,14 @@ func New(host string, port int, password string, options ...Option) (*Rcon, erro
|
|||||||
o(r)
|
o(r)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = r.login(); err != nil {
|
if err = r.Login(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return r, nil
|
return r, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r Rcon) login() error {
|
func (r Rcon) Login() error {
|
||||||
timeout := time.After(r.loginTimeout)
|
timeout := time.After(r.loginTimeout)
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
@ -80,21 +78,16 @@ func (r Rcon) login() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r Rcon) Send(cmdWithArgs string) (string, error) {
|
func (r Rcon) Send(cmd string) (string, error) {
|
||||||
cmd, _, _ := strings.Cut(string(cmdWithArgs), " ")
|
|
||||||
timeout, ok := r.timeouts[cmd]
|
timeout, ok := r.timeouts[cmd]
|
||||||
if !ok {
|
if !ok {
|
||||||
timeout = r.defaultTimeout
|
timeout = r.defaultTimeout
|
||||||
} else {
|
|
||||||
log.Debugf("%s in timeouts map, using timeout %v", cmd, timeout)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
respChan := make(chan string)
|
respChan := make(chan string)
|
||||||
errChan := make(chan error)
|
errChan := make(chan error)
|
||||||
|
|
||||||
go r.listen(timeout, respChan, errChan)
|
go r.listen(timeout, respChan, errChan)
|
||||||
|
_, err := r.conn.Write(r.request.Encode(cmd))
|
||||||
_, err := r.conn.Write(r.request.Encode(cmdWithArgs))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@ -103,7 +96,7 @@ func (r Rcon) Send(cmdWithArgs string) (string, error) {
|
|||||||
case err := <-errChan:
|
case err := <-errChan:
|
||||||
return "", err
|
return "", err
|
||||||
case resp := <-respChan:
|
case resp := <-respChan:
|
||||||
return resp, nil
|
return strings.TrimPrefix(resp, string(r.response.Header())), nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user