Skip to content

Commit

Permalink
idou: initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
lactyy committed Aug 23, 2024
0 parents commit 4add98c
Show file tree
Hide file tree
Showing 21 changed files with 2,341 additions and 0 deletions.
33 changes: 33 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# If you prefer the allow list template instead of the deny list, see community template:
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
#
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib

# Test binary, built with `go test -c`
*.test

# Output of the go coverage tool, specifically when used with LiteIDE
*.out

# Dependency directories (remove the comment below to include it)
# vendor/

# Go workspace file
go.work
go.work.sum

# env file
.env

auth.cred

/config.toml
/db
/resources

/.idea
674 changes: 674 additions & 0 deletions LICENSE

Large diffs are not rendered by default.

29 changes: 29 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# idou

A server tool that allows to you join any external server from friend list.

I heard a rumor that a world allows to connect external servers are no longer usable since 1.21.20, so I made one.
1.21.20 でコンソールなどで特集サーバー以外に接続するワールドが使えなくなった, という噂を聞いたので作ってみました. とても適当です.

This server/tool can be hosted just like opening a world, with no port forwarding required to other players to join.
このサーバー/ツールは手軽にワールドを開く感覚で, ポート開放不要で使用できます.

Compared to other methods (like DNS forwarding), You can just add a friend, and that's it.
従来の方法 (DNSなど) に比べて, フレンドを追加するだけで大丈夫です.

## 使い方 / How to use
If you have Go installed on your device, you can run ``go install github.com/lactyy/idou@latest``, then run ``idou`` from command-line.

If you haven't installed Go on your device, you can download an executable from Releases tab, then run ``./idou``.

The location where you ran the command will be the data folder, which contains a database for remembering servers which players has added, and an auth.cred file that stores your credentials.

## 警告 / Warning
Your IP addresses will be broadcasted to the players connecting to the world via signaling. It is highly recommended to host this server on a separated network.

Please use a separate account for hosting this, to avoid getting flagged.

## Credits
Thanks to the authors of [df-mc/nethernet-spec](https://github.com/df-mc/nethernet-spec) to writing the specification.

A huge thanks to Da1z981 for joining the debugging, and the community for helping.
102 changes: 102 additions & 0 deletions catalog.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package main

import (
"fmt"
"golang.org/x/text/language"
"golang.org/x/text/message"
)

// TODO: Implement reading catalog from JSON files

func init() {
for tag, dictionary := range map[language.Tag]map[string]string{
language.English: {
"menu.title": "Menu",

"item.menu": "Open Menu",

"menu.add.title": "Add a server",
"menu.add.button": "Add a server",
"menu.add.alreadyExists": "§cThis server has already been added.",
"menu.add.success": "§aThe server has been added.",

"menu.remove.title": "Remove a server",
"menu.remove.button": "Remove a server",
"menu.remove.success": "§cThe server has been removed.",

"menu.connect.title": "Connect to a server",
"menu.connect.button": "Connect to a server",
"menu.connect.remember.text": "Remember this server",

"menu.settings.title": "Settings",
"menu.settings.button": "Settings",

"server.button.text": "%s",
"server.button.text.named": "%s (%s)",
"server.address.text": "Server Address",
"server.address.placeholder": "Address or address:port",
"server.name.text": "Server Name (Optional)",

"server.error.resolve": "§cError resolving server address: %s",
"server.error.limit": "§cYou can add up to %d servers.",

"command.language.error.parse": "§cError parsing language: %s",

"language.unsupported": "§cUnsupported language: %s",
"language.changed": "§aThe language has been changed to %s.",
"language.auto": "§aWe have set your language to %s, based on your game language. You can change your language by opening settings.",

"menu.language.title": "Change language",
"menu.language.button": "Change language",
"menu.language.dropdown.text": "Please select a language:",
"menu.language.dropdown.option": "%s (%s)",
},
language.Japanese: {
"menu.title": "メニュー",

"item.menu": "メニューを開く",

"menu.add.title": "サーバーを追加",
"menu.add.button": "サーバーを追加",
"menu.add.alreadyExists": "§cこのサーバーは既に追加されています.",
"menu.add.success": "§aサーバーが追加されました.",

"menu.remove.title": "サーバーを削除",
"menu.remove.button": "サーバーを削除",
"menu.remove.success": "§cサーバーが削除されました.",

"menu.connect.title": "サーバーに接続",
"menu.connect.button": "サーバーに接続",
"menu.connect.remember.text": "このサーバーを記憶する",

"menu.settings.title": "設定",
"menu.settings.button": "設定",

"server.button.text": "%s",
"server.button.text.named": "%s (%s)",
"server.address.text": "サーバーのアドレス",
"server.address.placeholder": "アドレス, または アドレス:ポート",
"server.name.text": "サーバーの名前 (省略可)",

"server.error.resolve": "§cサーバーアドレスを解決できませんでした: %s",
"server.error.limit": "§c追加できるサーバーは最大 %d 個までです.",

"command.language.error.parse": "§c無効な言語です: %s",

"language.unsupported": "§cサポートされていない言語です: %s",
"language.changed": "§a言語を %s に変更しました.",
"language.auto": "§aゲーム内の言語に基づいて, 言語を %s に変更しました. メニューから設定を開くことで, 言語を変更できます.",

"menu.language.title": "言語を変更",
"menu.language.button": "言語を変更",
"menu.language.dropdown.text": "言語を選択してください:",
"menu.language.dropdown.option": "%s (%s)",
},
} {
for key, msg := range dictionary {
if err := message.SetString(tag, key, msg); err != nil {
panic(fmt.Sprintf("%s: error registering catalog message: %q (%q)", tag, key, msg))
}
}
}
}
10 changes: 10 additions & 0 deletions command.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package main

import (
"github.com/df-mc/dragonfly/server/cmd"
)

func init() {
cmd.Register(cmd.New("transfer", "Transfers you to a server", nil, transferCommand{}))
cmd.Register(cmd.New("settings", "Change settings", nil, settingsCommand{}, languageCommand{}))
}
69 changes: 69 additions & 0 deletions command_language.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package main

import (
"github.com/df-mc/dragonfly/server/cmd"
"github.com/df-mc/dragonfly/server/player"
"golang.org/x/text/language"
"golang.org/x/text/language/display"
"golang.org/x/text/message"
"slices"
)

type languageCommand struct {
Sub cmd.SubCommand `cmd:"language"`

Value cmd.Optional[supportedLanguage] `cmd:"value"`
}

func (c languageCommand) Run(src cmd.Source, o *cmd.Output) {
p := src.(*player.Player)
v, _ := handlers.Load(p.UUID())
h := v.(*handler)
m := h.m.Load()

if value, ok := c.Value.Load(); ok {
tag, err := language.Parse(string(value))
if err != nil {
o.Error(m.Sprintf("command.language.error.parse", err))
return
}
if !slices.Contains(message.DefaultCatalog.Languages(), tag) {
o.Error(m.Sprintf("language.unsupported", display.Self.Name(tag)))
return
}
h.m.Store(message.NewPrinter(tag))

prof := h.prof.Load()
prof.Language = tag
h.prof.Store(prof)

o.Printf(h.m.Load().Sprintf("language.changed", display.Self.Name(tag)))
} else {
p.SendForm(newLanguageForm(m, h.prof.Load()))
}
}

func (c languageCommand) Allow(src cmd.Source) bool {
p, ok := src.(*player.Player)
if !ok {
o := new(cmd.Output)
o.Errorf("This command can only be executed from in-game.")
src.SendCommandOutput(o)
return false
}
_, ok = handlers.Load(p.UUID())
return ok
}

type supportedLanguage string

func (supportedLanguage) Type() string { return "language" }

func (e supportedLanguage) Options(cmd.Source) []string {
supported := message.DefaultCatalog.Languages()
s := make([]string, len(supported))
for i, tag := range supported {
s[i] = tag.String()
}
return s
}
22 changes: 22 additions & 0 deletions command_settings.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package main

import (
"github.com/df-mc/dragonfly/server/cmd"
"github.com/df-mc/dragonfly/server/player"
)

type settingsCommand struct{}

func (c settingsCommand) Run(src cmd.Source, o *cmd.Output) {
p, ok := src.(*player.Player)
if !ok {
return
}
v, ok := handlers.Load(p.UUID())
if !ok {
return
}
h := v.(*handler)

p.SendForm(newSettingsForm(h.m.Load(), h.prof.Load()))
}
70 changes: 70 additions & 0 deletions command_transfer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package main

import (
"fmt"
"github.com/df-mc/dragonfly/server/cmd"
"github.com/df-mc/dragonfly/server/player"
"net"
"reflect"
)

type transferCommand struct {
Address rememberedAddress `cmd:"address"`
}

func (c transferCommand) Run(src cmd.Source, o *cmd.Output) {
p := src.(*player.Player)
v, _ := handlers.Load(p.UUID())
h := v.(*handler)
m := h.m.Load()

if err := p.Transfer(addressWithPort(string(c.Address))); err != nil {
o.Errorf(m.Sprintf("server.error.resolve", err))
}
}

func (c transferCommand) Allow(src cmd.Source) bool {
p, ok := src.(*player.Player)
if !ok {
o := new(cmd.Output)
o.Errorf("This command can only be executed from in-game.")
src.SendCommandOutput(o)
return false
}
_, ok = handlers.Load(p.UUID())
return ok
}

type rememberedAddress string

func (rememberedAddress) Type() string { return "address" }

func (param rememberedAddress) Parse(line *cmd.Line, v reflect.Value) error {
arg, ok := line.Next()
if !ok {
return cmd.ErrInsufficientArgs
}
if _, _, err := net.SplitHostPort(addressWithPort(arg)); err != nil {
return fmt.Errorf("unable to parse server address: %w", err)
}
v.SetString(arg)
return nil
}

func (param rememberedAddress) Options(src cmd.Source) []string {
p, ok := src.(*player.Player)
if !ok {
return nil
}
v, ok := handlers.Load(p.UUID())
if !ok {
return nil
}
h := v.(*handler)
servers := h.prof.Load().Servers
s := make([]string, len(servers))
for i, server := range servers {
s[i] = server.Address.String()
}
return s
}
Loading

0 comments on commit 4add98c

Please sign in to comment.