Begin writing code for reading and writing packets
This commit is contained in:
commit
fae5bb3139
188
.credo.exs
Normal file
188
.credo.exs
Normal file
@ -0,0 +1,188 @@
|
|||||||
|
%{
|
||||||
|
#
|
||||||
|
# You can have as many configs as you like in the `configs:` field.
|
||||||
|
configs: [
|
||||||
|
%{
|
||||||
|
#
|
||||||
|
# Run any config using `mix credo -C <name>`. If no config name is given
|
||||||
|
# "default" is used.
|
||||||
|
#
|
||||||
|
name: "default",
|
||||||
|
#
|
||||||
|
# These are the files included in the analysis:
|
||||||
|
files: %{
|
||||||
|
#
|
||||||
|
# You can give explicit globs or simply directories.
|
||||||
|
# In the latter case `**/*.{ex,exs}` will be used.
|
||||||
|
#
|
||||||
|
included: [
|
||||||
|
"lib/",
|
||||||
|
"src/",
|
||||||
|
"test/",
|
||||||
|
],
|
||||||
|
excluded: [~r"/_build/", ~r"/deps/"]
|
||||||
|
},
|
||||||
|
plugins: [],
|
||||||
|
requires: [],
|
||||||
|
strict: false,
|
||||||
|
parse_timeout: 5000,
|
||||||
|
color: true,
|
||||||
|
#
|
||||||
|
# You can customize the parameters of any check by adding a second element
|
||||||
|
# to the tuple.
|
||||||
|
#
|
||||||
|
# To disable a check put `false` as second element:
|
||||||
|
#
|
||||||
|
# {Credo.Check.Design.DuplicatedCode, false}
|
||||||
|
#
|
||||||
|
checks: %{
|
||||||
|
enabled: [
|
||||||
|
#
|
||||||
|
## Consistency Checks
|
||||||
|
#
|
||||||
|
{Credo.Check.Consistency.ExceptionNames, []},
|
||||||
|
{Credo.Check.Consistency.LineEndings, []},
|
||||||
|
{Credo.Check.Consistency.ParameterPatternMatching, []},
|
||||||
|
{Credo.Check.Consistency.SpaceAroundOperators, []},
|
||||||
|
{Credo.Check.Consistency.SpaceInParentheses, []},
|
||||||
|
{Credo.Check.Consistency.TabsOrSpaces, []},
|
||||||
|
|
||||||
|
#
|
||||||
|
## Design Checks
|
||||||
|
#
|
||||||
|
# You can customize the priority of any check
|
||||||
|
# Priority values are: `low, normal, high, higher`
|
||||||
|
#
|
||||||
|
{Credo.Check.Design.AliasUsage,
|
||||||
|
[priority: :low, if_nested_deeper_than: 2, if_called_more_often_than: 0]},
|
||||||
|
{Credo.Check.Design.TagFIXME, []},
|
||||||
|
# You can also customize the exit_status of each check.
|
||||||
|
# If you don't want TODO comments to cause `mix credo` to fail, just
|
||||||
|
# set this value to 0 (zero).
|
||||||
|
#
|
||||||
|
{Credo.Check.Design.TagTODO, [exit_status: 2]},
|
||||||
|
|
||||||
|
#
|
||||||
|
## Readability Checks
|
||||||
|
#
|
||||||
|
{Credo.Check.Readability.AliasOrder, []},
|
||||||
|
{Credo.Check.Readability.FunctionNames, []},
|
||||||
|
{Credo.Check.Readability.LargeNumbers, []},
|
||||||
|
{Credo.Check.Readability.MaxLineLength, [priority: :low, max_length: 120]},
|
||||||
|
{Credo.Check.Readability.ModuleAttributeNames, []},
|
||||||
|
{Credo.Check.Readability.ModuleDoc, []},
|
||||||
|
{Credo.Check.Readability.ModuleNames, []},
|
||||||
|
{Credo.Check.Readability.ParenthesesInCondition, []},
|
||||||
|
{Credo.Check.Readability.ParenthesesOnZeroArityDefs, []},
|
||||||
|
{Credo.Check.Readability.PipeIntoAnonymousFunctions, []},
|
||||||
|
{Credo.Check.Readability.PredicateFunctionNames, []},
|
||||||
|
{Credo.Check.Readability.PreferImplicitTry, []},
|
||||||
|
{Credo.Check.Readability.RedundantBlankLines, []},
|
||||||
|
{Credo.Check.Readability.Semicolons, []},
|
||||||
|
{Credo.Check.Readability.SpaceAfterCommas, []},
|
||||||
|
{Credo.Check.Readability.StringSigils, []},
|
||||||
|
{Credo.Check.Readability.TrailingBlankLine, []},
|
||||||
|
{Credo.Check.Readability.TrailingWhiteSpace, []},
|
||||||
|
{Credo.Check.Readability.UnnecessaryAliasExpansion, []},
|
||||||
|
{Credo.Check.Readability.VariableNames, []},
|
||||||
|
{Credo.Check.Readability.WithSingleClause, []},
|
||||||
|
|
||||||
|
#
|
||||||
|
## Refactoring Opportunities
|
||||||
|
#
|
||||||
|
{Credo.Check.Refactor.Apply, []},
|
||||||
|
{Credo.Check.Refactor.CondStatements, []},
|
||||||
|
{Credo.Check.Refactor.CyclomaticComplexity, []},
|
||||||
|
{Credo.Check.Refactor.FilterCount, []},
|
||||||
|
{Credo.Check.Refactor.FilterFilter, []},
|
||||||
|
{Credo.Check.Refactor.FunctionArity, []},
|
||||||
|
{Credo.Check.Refactor.LongQuoteBlocks, []},
|
||||||
|
{Credo.Check.Refactor.MapJoin, []},
|
||||||
|
{Credo.Check.Refactor.MatchInCondition, []},
|
||||||
|
{Credo.Check.Refactor.NegatedConditionsInUnless, []},
|
||||||
|
{Credo.Check.Refactor.NegatedConditionsWithElse, []},
|
||||||
|
{Credo.Check.Refactor.Nesting, []},
|
||||||
|
{Credo.Check.Refactor.RedundantWithClauseResult, []},
|
||||||
|
{Credo.Check.Refactor.RejectReject, []},
|
||||||
|
{Credo.Check.Refactor.UnlessWithElse, []},
|
||||||
|
{Credo.Check.Refactor.WithClauses, []},
|
||||||
|
|
||||||
|
#
|
||||||
|
## Warnings
|
||||||
|
#
|
||||||
|
{Credo.Check.Warning.ApplicationConfigInModuleAttribute, []},
|
||||||
|
{Credo.Check.Warning.BoolOperationOnSameValues, []},
|
||||||
|
{Credo.Check.Warning.Dbg, []},
|
||||||
|
{Credo.Check.Warning.ExpensiveEmptyEnumCheck, []},
|
||||||
|
{Credo.Check.Warning.IExPry, []},
|
||||||
|
{Credo.Check.Warning.IoInspect, []},
|
||||||
|
{Credo.Check.Warning.MissedMetadataKeyInLoggerConfig, []},
|
||||||
|
{Credo.Check.Warning.OperationOnSameValues, []},
|
||||||
|
{Credo.Check.Warning.OperationWithConstantResult, []},
|
||||||
|
{Credo.Check.Warning.RaiseInsideRescue, []},
|
||||||
|
{Credo.Check.Warning.SpecWithStruct, []},
|
||||||
|
{Credo.Check.Warning.UnsafeExec, []},
|
||||||
|
{Credo.Check.Warning.UnusedEnumOperation, []},
|
||||||
|
{Credo.Check.Warning.UnusedFileOperation, []},
|
||||||
|
{Credo.Check.Warning.UnusedKeywordOperation, []},
|
||||||
|
{Credo.Check.Warning.UnusedListOperation, []},
|
||||||
|
{Credo.Check.Warning.UnusedPathOperation, []},
|
||||||
|
{Credo.Check.Warning.UnusedRegexOperation, []},
|
||||||
|
{Credo.Check.Warning.UnusedStringOperation, []},
|
||||||
|
{Credo.Check.Warning.UnusedTupleOperation, []},
|
||||||
|
{Credo.Check.Warning.WrongTestFileExtension, []}
|
||||||
|
],
|
||||||
|
disabled: [
|
||||||
|
#
|
||||||
|
# Checks scheduled for next check update (opt-in for now)
|
||||||
|
{Credo.Check.Refactor.UtcNowTruncate, []},
|
||||||
|
|
||||||
|
#
|
||||||
|
# Controversial and experimental checks (opt-in, just move the check to `:enabled`
|
||||||
|
# and be sure to use `mix credo --strict` to see low priority checks)
|
||||||
|
#
|
||||||
|
{Credo.Check.Consistency.MultiAliasImportRequireUse, []},
|
||||||
|
{Credo.Check.Consistency.UnusedVariableNames, []},
|
||||||
|
{Credo.Check.Design.DuplicatedCode, []},
|
||||||
|
{Credo.Check.Design.SkipTestWithoutComment, []},
|
||||||
|
{Credo.Check.Readability.AliasAs, []},
|
||||||
|
{Credo.Check.Readability.BlockPipe, []},
|
||||||
|
{Credo.Check.Readability.ImplTrue, []},
|
||||||
|
{Credo.Check.Readability.MultiAlias, []},
|
||||||
|
{Credo.Check.Readability.NestedFunctionCalls, []},
|
||||||
|
{Credo.Check.Readability.OneArityFunctionInPipe, []},
|
||||||
|
{Credo.Check.Readability.OnePipePerLine, []},
|
||||||
|
{Credo.Check.Readability.SeparateAliasRequire, []},
|
||||||
|
{Credo.Check.Readability.SingleFunctionToBlockPipe, []},
|
||||||
|
{Credo.Check.Readability.SinglePipe, []},
|
||||||
|
{Credo.Check.Readability.Specs, []},
|
||||||
|
{Credo.Check.Readability.StrictModuleLayout, []},
|
||||||
|
{Credo.Check.Readability.WithCustomTaggedTuple, []},
|
||||||
|
{Credo.Check.Refactor.ABCSize, []},
|
||||||
|
{Credo.Check.Refactor.AppendSingleItem, []},
|
||||||
|
{Credo.Check.Refactor.DoubleBooleanNegation, []},
|
||||||
|
{Credo.Check.Refactor.FilterReject, []},
|
||||||
|
{Credo.Check.Refactor.IoPuts, []},
|
||||||
|
{Credo.Check.Refactor.MapMap, []},
|
||||||
|
{Credo.Check.Refactor.ModuleDependencies, []},
|
||||||
|
{Credo.Check.Refactor.NegatedIsNil, []},
|
||||||
|
{Credo.Check.Refactor.PassAsyncInTestCases, []},
|
||||||
|
{Credo.Check.Refactor.PipeChainStart, []},
|
||||||
|
{Credo.Check.Refactor.RejectFilter, []},
|
||||||
|
{Credo.Check.Refactor.VariableRebinding, []},
|
||||||
|
{Credo.Check.Warning.LazyLogging, []},
|
||||||
|
{Credo.Check.Warning.LeakyEnvironment, []},
|
||||||
|
{Credo.Check.Warning.MapGetUnsafePass, []},
|
||||||
|
{Credo.Check.Warning.MixEnv, []},
|
||||||
|
{Credo.Check.Warning.UnsafeToAtom, []}
|
||||||
|
|
||||||
|
# {Credo.Check.Refactor.MapInto, []},
|
||||||
|
|
||||||
|
#
|
||||||
|
# Custom checks can be created using `mix credo.gen.check`.
|
||||||
|
#
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
4
.formatter.exs
Normal file
4
.formatter.exs
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
# Used by "mix format"
|
||||||
|
[
|
||||||
|
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
|
||||||
|
]
|
32
.gitignore
vendored
Normal file
32
.gitignore
vendored
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
# The directory Mix will write compiled artifacts to.
|
||||||
|
/_build/
|
||||||
|
|
||||||
|
# If you run "mix test --cover", coverage assets end up here.
|
||||||
|
/cover/
|
||||||
|
|
||||||
|
# The directory Mix downloads your dependencies sources to.
|
||||||
|
/deps/
|
||||||
|
|
||||||
|
# Where third-party dependencies like ExDoc output generated docs.
|
||||||
|
/doc/
|
||||||
|
|
||||||
|
# Ignore .fetch files in case you like to edit your project deps locally.
|
||||||
|
/.fetch
|
||||||
|
|
||||||
|
# If the VM crashes, it generates a dump, let's ignore it too.
|
||||||
|
erl_crash.dump
|
||||||
|
|
||||||
|
# Also ignore archive artifacts (built via "mix archive.build").
|
||||||
|
*.ez
|
||||||
|
|
||||||
|
# Ignore package tarball (built via "mix hex.build").
|
||||||
|
amethyst-*.tar
|
||||||
|
|
||||||
|
# Temporary files, for example, from tests.
|
||||||
|
/tmp/
|
||||||
|
|
||||||
|
# Language server logs
|
||||||
|
.elixir_ls/
|
||||||
|
|
||||||
|
# Direnv
|
||||||
|
.direnv
|
34
.vscode/launch.json
vendored
Normal file
34
.vscode/launch.json
vendored
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
// Use IntelliSense to learn about possible attributes.
|
||||||
|
// Hover to view descriptions of existing attributes.
|
||||||
|
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"type": "mix_task",
|
||||||
|
"name": "mix (Default task)",
|
||||||
|
"request": "launch",
|
||||||
|
"projectDir": "${workspaceRoot}",
|
||||||
|
"task": "run",
|
||||||
|
"taskArgs": [
|
||||||
|
"--no-halt"
|
||||||
|
],
|
||||||
|
"exitAfterTaskReturns": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "mix_task",
|
||||||
|
"name": "mix test",
|
||||||
|
"request": "launch",
|
||||||
|
"task": "test",
|
||||||
|
"taskArgs": [
|
||||||
|
"--trace"
|
||||||
|
],
|
||||||
|
"startApps": true,
|
||||||
|
"projectDir": "${workspaceRoot}",
|
||||||
|
"requireFiles": [
|
||||||
|
"test/**/test_helper.exs",
|
||||||
|
"test/**/*_test.exs"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
21
README.md
Normal file
21
README.md
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
# Amethyst
|
||||||
|
|
||||||
|
**TODO: Add description**
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
If [available in Hex](https://hex.pm/docs/publish), the package can be installed
|
||||||
|
by adding `amethyst` to your list of dependencies in `mix.exs`:
|
||||||
|
|
||||||
|
```elixir
|
||||||
|
def deps do
|
||||||
|
[
|
||||||
|
{:amethyst, "~> 0.1.0"}
|
||||||
|
]
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc)
|
||||||
|
and published on [HexDocs](https://hexdocs.pm). Once published, the docs can
|
||||||
|
be found at <https://hexdocs.pm/amethyst>.
|
||||||
|
|
4
config/runtime.exs
Normal file
4
config/runtime.exs
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
import Config
|
||||||
|
|
||||||
|
config :amethyst,
|
||||||
|
port: 25599 # Bogus port for testing, avoids unexpected conflicts
|
64
flake.lock
Normal file
64
flake.lock
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"nix-github-actions": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1720066371,
|
||||||
|
"narHash": "sha256-uPlLYH2S0ACj0IcgaK9Lsf4spmJoGejR9DotXiXSBZQ=",
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "nix-github-actions",
|
||||||
|
"rev": "622f829f5fe69310a866c8a6cd07e747c44ef820",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "nix-github-actions",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1720181791,
|
||||||
|
"narHash": "sha256-i4vJL12/AdyuQuviMMd1Hk2tsGt02hDNhA0Zj1m16N8=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "4284c2b73c8bce4b46a6adf23e16d9e2ec8da4bb",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixpkgs-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"nix-github-actions": "nix-github-actions",
|
||||||
|
"nixpkgs": "nixpkgs",
|
||||||
|
"systems": "systems"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"systems": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1681028828,
|
||||||
|
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
91
flake.nix
Normal file
91
flake.nix
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
{
|
||||||
|
description = "Amethyst - Experimental Minecraft server in Elixir";
|
||||||
|
|
||||||
|
inputs = {
|
||||||
|
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
|
||||||
|
|
||||||
|
nix-github-actions.url = "github:nix-community/nix-github-actions";
|
||||||
|
nix-github-actions.inputs.nixpkgs.follows = "nixpkgs";
|
||||||
|
|
||||||
|
systems.url = "github:nix-systems/default";
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs = {
|
||||||
|
self,
|
||||||
|
nixpkgs,
|
||||||
|
systems,
|
||||||
|
nix-github-actions,
|
||||||
|
...
|
||||||
|
} @ inputs: let
|
||||||
|
inherit (nixpkgs) lib;
|
||||||
|
|
||||||
|
# Set the Erlang version
|
||||||
|
erlangVersion = "erlang_25";
|
||||||
|
# Set the Elixir version
|
||||||
|
elixirVersion = "elixir_1_17";
|
||||||
|
|
||||||
|
eachSystem = f:
|
||||||
|
nixpkgs.lib.genAttrs (import systems) (
|
||||||
|
system:
|
||||||
|
f (import nixpkgs {
|
||||||
|
inherit system;
|
||||||
|
overlays = [
|
||||||
|
(final: _: let
|
||||||
|
erlang = final.beam.interpreters.${erlangVersion};
|
||||||
|
beamPackages = final.beam.packages.${erlangVersion};
|
||||||
|
elixir = beamPackages.${elixirVersion};
|
||||||
|
in {
|
||||||
|
inherit erlang elixir;
|
||||||
|
inherit (beamPackages) elixir-ls hex;
|
||||||
|
inherit (beamPackages) mixRelease;
|
||||||
|
})
|
||||||
|
];
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
#secrets = import ./secrets.nix;
|
||||||
|
|
||||||
|
package = env: pkgs: (pkgs.beamPackages.mixRelease {
|
||||||
|
pname = "amethyst";
|
||||||
|
version = "0.1.0";
|
||||||
|
src = ./.;
|
||||||
|
elixir = pkgs.elixir;
|
||||||
|
mixEnv = env;
|
||||||
|
erlangDeterministicBuilds = false; # Technically less pure but allows doctests
|
||||||
|
removeCookie = false; # Insecure; Access to the file system can allow the cookie to be read and provides remote control of the Erlang VM
|
||||||
|
mixNixDeps = import ./mix.nix {inherit lib; beamPackages = pkgs.beamPackages;};
|
||||||
|
}).overrideAttrs (old: {
|
||||||
|
preInstall = ''
|
||||||
|
# Run automated tests
|
||||||
|
mix test --no-deps-check --no-start --color
|
||||||
|
'';
|
||||||
|
});
|
||||||
|
|
||||||
|
in {
|
||||||
|
packages = eachSystem (pkgs:
|
||||||
|
{
|
||||||
|
default = package "prod" pkgs;
|
||||||
|
dev = package "dev" pkgs;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
checks = self.packages;
|
||||||
|
|
||||||
|
githubActions = nix-github-actions.lib.mkGithubMatrix {
|
||||||
|
checks = nixpkgs.lib.getAttrs [ "x86_64-linux" ] self.packages;
|
||||||
|
};
|
||||||
|
|
||||||
|
devShells = eachSystem (
|
||||||
|
pkgs: {
|
||||||
|
default = pkgs.mkShell {
|
||||||
|
buildInputs = with pkgs; [
|
||||||
|
erlang
|
||||||
|
elixir
|
||||||
|
elixir-ls
|
||||||
|
mix2nix
|
||||||
|
];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
20
lib/amethyst.ex
Normal file
20
lib/amethyst.ex
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
defmodule Amethyst.Application do
|
||||||
|
# See https://hexdocs.pm/elixir/Application.html
|
||||||
|
# for more information on OTP Applications
|
||||||
|
@moduledoc false
|
||||||
|
|
||||||
|
use Application
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def start(_type, _args) do
|
||||||
|
children = [
|
||||||
|
Supervisor.child_spec({Task, fn -> Amethyst.TCPListener.accept(Application.fetch_env!(:amethyst, :port)) end}, restart: :permanent),
|
||||||
|
{Task.Supervisor, name: Amethyst.ConnectionSupervisor},
|
||||||
|
]
|
||||||
|
|
||||||
|
# See https://hexdocs.pm/elixir/Supervisor.html
|
||||||
|
# for other strategies and supported options
|
||||||
|
opts = [strategy: :one_for_one, name: Amethyst.Supervisor]
|
||||||
|
Supervisor.start_link(children, opts)
|
||||||
|
end
|
||||||
|
end
|
35
lib/apps/tcp_listener.ex
Normal file
35
lib/apps/tcp_listener.ex
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
defmodule Amethyst.TCPListener do
|
||||||
|
require Logger
|
||||||
|
@moduledoc """
|
||||||
|
This module listens for TCP connections and creates server tasks to handle them.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def accept(port) do
|
||||||
|
{:ok, socket} = :gen_tcp.listen(port, [:binary, packet: 0, active: false, reuseaddr: true])
|
||||||
|
Logger.info("Listening on port #{port}")
|
||||||
|
loop_acceptor(socket)
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec loop_acceptor(socket :: :gen_tcp.socket()) :: no_return()
|
||||||
|
defp loop_acceptor(socket) do
|
||||||
|
{:ok, client} = :gen_tcp.accept(socket)
|
||||||
|
{:ok, pid} = Task.Supervisor.start_child(Amethyst.ConnectionSupervisor, fn -> serve(client) end)
|
||||||
|
:ok = :gen_tcp.controlling_process(client, pid)
|
||||||
|
Logger.info("Received connection from #{inspect(client)}, assigned to PID #{inspect(pid)}")
|
||||||
|
loop_acceptor(socket)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp serve(socket) do
|
||||||
|
socket |> read_line() |> write_line(socket)
|
||||||
|
serve(socket)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp read_line(socket) do
|
||||||
|
{:ok, data} = :gen_tcp.recv(socket, 0)
|
||||||
|
data
|
||||||
|
end
|
||||||
|
|
||||||
|
defp write_line(line, socket) do
|
||||||
|
:gen_tcp.send(socket, line)
|
||||||
|
end
|
||||||
|
end
|
109
lib/data.ex
Normal file
109
lib/data.ex
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
defmodule Amethyst.Minecraft.Write do
|
||||||
|
import Bitwise
|
||||||
|
|
||||||
|
@moduledoc """
|
||||||
|
This module contains functions for writing certain Minecraft data types which are more complex
|
||||||
|
than simple binary data.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def varint(value) when value in -2_147_483_648..2_147_483_647 do
|
||||||
|
<<value::32-unsigned>> = <<value::32-signed>> # This is a trick to allow the arithmetic shift to act as a logical shift
|
||||||
|
varnum("", value)
|
||||||
|
end
|
||||||
|
|
||||||
|
def varint(_) do
|
||||||
|
raise ArgumentError, "Value is out of range for a varint"
|
||||||
|
end
|
||||||
|
|
||||||
|
def varlong(value) when value in -9_223_372_036_854_775_808..9_223_372_036_854_775_807
|
||||||
|
do
|
||||||
|
<<value::64-unsigned>> = <<value::64-signed>>
|
||||||
|
varnum("", value)
|
||||||
|
end
|
||||||
|
|
||||||
|
def varlong(_) do
|
||||||
|
raise ArgumentError, "Value is out of range for a varlong"
|
||||||
|
end
|
||||||
|
|
||||||
|
defp varnum(acc, value) when value in 0..127 do
|
||||||
|
acc <> <<0::1, value::7-big>>
|
||||||
|
end
|
||||||
|
|
||||||
|
defp varnum(acc, value) do
|
||||||
|
acc <> <<1::1, value::7-big>> |> varnum(value >>> 7)
|
||||||
|
end
|
||||||
|
|
||||||
|
def string(value) do
|
||||||
|
<<varint(byte_size(value))::binary, value::binary>>
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defmodule Amethyst.Minecraft.Read do
|
||||||
|
import Bitwise
|
||||||
|
|
||||||
|
@moduledoc """
|
||||||
|
This module contains functions for reading Minecraft data. Unlike Amethyst.Minecraft.Write, this
|
||||||
|
includes all supported data types in order to make the interface more consistent.
|
||||||
|
|
||||||
|
These functions allow you to chain them into eachother, at the end they will produce a list of all the
|
||||||
|
values they have read.
|
||||||
|
|
||||||
|
You may use the helper function Amethyst.Minecraft.Read.start/1 to start the chain and Amethyst.Minecraft.Read.stop/1
|
||||||
|
to get the data out of the chain. If you pass the option `reverse: true` to stop/1, the list will be reversed, this may
|
||||||
|
provide better performance if you do not mind the inconvenience of the reversed list.
|
||||||
|
|
||||||
|
iex> alias Amethyst.Minecraft.Read
|
||||||
|
iex> [_, _, _] = Read.start(<<1, 999::16, 64>>) |> Read.bool() |> Read.short() |> Read.byte() |> Read.stop()
|
||||||
|
[true, 999, 64]
|
||||||
|
"""
|
||||||
|
|
||||||
|
def start(binary) do
|
||||||
|
{[], binary}
|
||||||
|
end
|
||||||
|
|
||||||
|
def stop({acc, rest}, opts \\ []) do
|
||||||
|
if Keyword.get(opts, :force_empty, false) and bit_size(rest) != 0 do
|
||||||
|
raise ArgumentError, "Expected no more data, but there is still #{bit_size(rest)} bits left"
|
||||||
|
end
|
||||||
|
case Keyword.get(opts, :reverse, false) do
|
||||||
|
true -> acc
|
||||||
|
false -> Enum.reverse(acc)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def bool({acc, <<value, rest::bitstring>>}) do
|
||||||
|
{[value != 0 | acc], rest}
|
||||||
|
end
|
||||||
|
|
||||||
|
def byte({acc, <<value::big-signed, rest::binary>>}) do
|
||||||
|
{[value | acc], rest}
|
||||||
|
end
|
||||||
|
|
||||||
|
def ubyte({acc, <<value::big-unsigned, rest::binary>>}) do
|
||||||
|
{[value | acc], rest}
|
||||||
|
end
|
||||||
|
|
||||||
|
def short({acc, <<value::16-big-signed, rest::binary>>}) do
|
||||||
|
{[value | acc], rest}
|
||||||
|
end
|
||||||
|
|
||||||
|
def ushort({acc, <<value::16-big-unsigned, rest::binary>>}) do
|
||||||
|
{[value | acc], rest}
|
||||||
|
end
|
||||||
|
|
||||||
|
def int({acc, <<value::32-big-signed, rest::binary>>}) do
|
||||||
|
{[value | acc], rest}
|
||||||
|
end
|
||||||
|
|
||||||
|
def long({acc, <<value::64-big-signed, rest::binary>>}) do
|
||||||
|
{[value | acc], rest}
|
||||||
|
end
|
||||||
|
|
||||||
|
def float({acc, <<value::32-float-big, rest::binary>>}) do
|
||||||
|
{[value | acc], rest}
|
||||||
|
end
|
||||||
|
|
||||||
|
def double({acc, <<value::64-float-big, rest::binary>>}) do
|
||||||
|
{[value | acc], rest}
|
||||||
|
end
|
||||||
|
end
|
5
lib/servers/stage1.ex
Normal file
5
lib/servers/stage1.ex
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
defmodule Amethyst.Server.Stage1 do
|
||||||
|
@moduledoc """
|
||||||
|
This module contains the stage 1 (Handshaking) server logic.
|
||||||
|
"""
|
||||||
|
end
|
28
mix.exs
Normal file
28
mix.exs
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
defmodule Amethyst.MixProject do
|
||||||
|
use Mix.Project
|
||||||
|
|
||||||
|
def project do
|
||||||
|
[
|
||||||
|
app: :amethyst,
|
||||||
|
version: "0.1.0",
|
||||||
|
elixir: "~> 1.17",
|
||||||
|
start_permanent: Mix.env() == :prod,
|
||||||
|
deps: deps()
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
# Run "mix help compile.app" to learn about applications.
|
||||||
|
def application do
|
||||||
|
[
|
||||||
|
extra_applications: [:logger],
|
||||||
|
mod: {Amethyst.Application, []}
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
# Run "mix help deps" to learn about dependencies.
|
||||||
|
defp deps do
|
||||||
|
[
|
||||||
|
{:credo, "~> 1.7", only: [:dev, :test], runtime: false}
|
||||||
|
]
|
||||||
|
end
|
||||||
|
end
|
6
mix.lock
Normal file
6
mix.lock
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
%{
|
||||||
|
"bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"},
|
||||||
|
"credo": {:hex, :credo, "1.7.7", "771445037228f763f9b2afd612b6aa2fd8e28432a95dbbc60d8e03ce71ba4446", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "8bc87496c9aaacdc3f90f01b7b0582467b69b4bd2441fe8aae3109d843cc2f2e"},
|
||||||
|
"file_system": {:hex, :file_system, "1.0.0", "b689cc7dcee665f774de94b5a832e578bd7963c8e637ef940cd44327db7de2cd", [:mix], [], "hexpm", "6752092d66aec5a10e662aefeed8ddb9531d79db0bc145bb8c40325ca1d8536d"},
|
||||||
|
"jason": {:hex, :jason, "1.4.3", "d3f984eeb96fe53b85d20e0b049f03e57d075b5acda3ac8d465c969a2536c17b", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "9a90e868927f7c777689baa16d86f4d0e086d968db5c05d917ccff6d443e58a3"},
|
||||||
|
}
|
64
mix.nix
Normal file
64
mix.nix
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
{ lib, beamPackages, overrides ? (x: y: {}) }:
|
||||||
|
|
||||||
|
let
|
||||||
|
buildRebar3 = lib.makeOverridable beamPackages.buildRebar3;
|
||||||
|
buildMix = lib.makeOverridable beamPackages.buildMix;
|
||||||
|
buildErlangMk = lib.makeOverridable beamPackages.buildErlangMk;
|
||||||
|
|
||||||
|
self = packages // (overrides self packages);
|
||||||
|
|
||||||
|
packages = with beamPackages; with self; {
|
||||||
|
bunt = buildMix rec {
|
||||||
|
name = "bunt";
|
||||||
|
version = "1.0.0";
|
||||||
|
|
||||||
|
src = fetchHex {
|
||||||
|
pkg = "bunt";
|
||||||
|
version = "${version}";
|
||||||
|
sha256 = "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5";
|
||||||
|
};
|
||||||
|
|
||||||
|
beamDeps = [];
|
||||||
|
};
|
||||||
|
|
||||||
|
credo = buildMix rec {
|
||||||
|
name = "credo";
|
||||||
|
version = "1.7.7";
|
||||||
|
|
||||||
|
src = fetchHex {
|
||||||
|
pkg = "credo";
|
||||||
|
version = "${version}";
|
||||||
|
sha256 = "8bc87496c9aaacdc3f90f01b7b0582467b69b4bd2441fe8aae3109d843cc2f2e";
|
||||||
|
};
|
||||||
|
|
||||||
|
beamDeps = [ bunt file_system jason ];
|
||||||
|
};
|
||||||
|
|
||||||
|
file_system = buildMix rec {
|
||||||
|
name = "file_system";
|
||||||
|
version = "1.0.0";
|
||||||
|
|
||||||
|
src = fetchHex {
|
||||||
|
pkg = "file_system";
|
||||||
|
version = "${version}";
|
||||||
|
sha256 = "6752092d66aec5a10e662aefeed8ddb9531d79db0bc145bb8c40325ca1d8536d";
|
||||||
|
};
|
||||||
|
|
||||||
|
beamDeps = [];
|
||||||
|
};
|
||||||
|
|
||||||
|
jason = buildMix rec {
|
||||||
|
name = "jason";
|
||||||
|
version = "1.4.3";
|
||||||
|
|
||||||
|
src = fetchHex {
|
||||||
|
pkg = "jason";
|
||||||
|
version = "${version}";
|
||||||
|
sha256 = "9a90e868927f7c777689baa16d86f4d0e086d968db5c05d917ccff6d443e58a3";
|
||||||
|
};
|
||||||
|
|
||||||
|
beamDeps = [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
in self
|
||||||
|
|
30
test/data_test.exs
Normal file
30
test/data_test.exs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
defmodule WriteTest do
|
||||||
|
use ExUnit.Case
|
||||||
|
doctest Amethyst.Minecraft.Write
|
||||||
|
doctest Amethyst.Minecraft.Read
|
||||||
|
|
||||||
|
test "writing a varint" do
|
||||||
|
assert Amethyst.Minecraft.Write.varint(0) == <<0x00>>
|
||||||
|
assert Amethyst.Minecraft.Write.varint(127) == <<0x7F>>
|
||||||
|
assert Amethyst.Minecraft.Write.varint(128) == <<0x80, 0x01>>
|
||||||
|
assert Amethyst.Minecraft.Write.varint(255) == <<0xFF, 0x01>>
|
||||||
|
assert Amethyst.Minecraft.Write.varint(2_147_483_647) == <<0xFF, 0xFF, 0xFF, 0xFF, 0x07>>
|
||||||
|
assert Amethyst.Minecraft.Write.varint(-1) == <<0xFF, 0xFF, 0xFF, 0xFF, 0x0F>>
|
||||||
|
assert Amethyst.Minecraft.Write.varint(-2_147_483_648) == <<0x80, 0x80, 0x80, 0x80, 0x08>>
|
||||||
|
end
|
||||||
|
|
||||||
|
test "writing a varlong" do
|
||||||
|
assert Amethyst.Minecraft.Write.varlong(0) == <<0x00>>
|
||||||
|
assert Amethyst.Minecraft.Write.varlong(127) == <<0x7F>>
|
||||||
|
assert Amethyst.Minecraft.Write.varlong(128) == <<0x80, 0x01>>
|
||||||
|
assert Amethyst.Minecraft.Write.varlong(255) == <<0xFF, 0x01>>
|
||||||
|
assert Amethyst.Minecraft.Write.varlong(2_147_483_647) == <<0xFF, 0xFF, 0xFF, 0xFF, 0x07>>
|
||||||
|
assert Amethyst.Minecraft.Write.varlong(9_223_372_036_854_775_807) == <<0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F>>
|
||||||
|
assert Amethyst.Minecraft.Write.varlong(-1) == <<0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01>>
|
||||||
|
end
|
||||||
|
|
||||||
|
test "writing a string" do
|
||||||
|
assert Amethyst.Minecraft.Write.string("Hello, world!") == <<0x0D, "Hello, world!">>
|
||||||
|
assert Amethyst.Minecraft.Write.string("") == <<0x00>>
|
||||||
|
end
|
||||||
|
end
|
1
test/test_helper.exs
Normal file
1
test/test_helper.exs
Normal file
@ -0,0 +1 @@
|
|||||||
|
ExUnit.start()
|
Loading…
Reference in New Issue
Block a user