elyby / chrly Goto Github PK
View Code? Open in Web Editor NEWLightweight implementation of Minecraft skins system server. It's packaged and distributed as a Docker image.
License: Apache License 2.0
Lightweight implementation of Minecraft skins system server. It's packaged and distributed as a Docker image.
License: Apache License 2.0
Now it usesely.skinsystem.{hostname}.app.
, but since this is a separate independent project, there should be no Ely.by mentioning.
15:12:20.382 ┃ 195.201.80.5 - - "GET /api/worker/mojang-uuid/blah" 200 - "Chrly/$APP_VERSION" "172.20.0.2"
Currently Ely.by SkinSystem doesn't work correctly with Mojang skins.
When skin is not found, it returns URL for the old version of Mojang API, such as "skins.minecraft.net/MinecraftSkins/%player%.png".
Most of offline-mode servers uses plugins for restoring Mojang skins: SkinsRestorer, Custom Skins Manager etc. They make request to Mojang API and get original skin URL, using network and CPU resources of dedicated server.
Now Ely.by authlib has the following algorithm (will describe partially simplifed):
Since Minecraft launcher "TL" uses Ely.by skin system by default, all the players have broken Mojang skins on offline-mode servers even with installed skin restoring plugin.
Here we can see logs of Minecraft client:
https://hastebin.brikster.ru/mocowewato.shell
My player with nickname Brikster has Mojang skin and it was restored by the server. Client got correct Mojang link with my skin texture, but finally chose broken Mojang link, that was returned by Ely.by's chrly.
Possible solution (until #11 will be completely solved):
In the last case (when profile, received by client, isn't empty, and player name is equals to name of profile), we can request Ely.by skin from chrly. Then, if chrly replied, that skin is not found, we shouldn't use broken link to a Mojang skin. Instead of that we can use textures, that was provided by the server.
{
"SKIN": {
"url": "https://ely.by/skins/s2682351/skin.png",
"metadata": {
"model": "slim"
}
},
"CAPE": {
"url": "http://example.com/cape.png"
}
}
Is there any way to get the avatar (head 2d image) of a player's skin by their username or uuid on png from the ely.by skinsystem database?
Like for example this link gives me this image:
The current implementation uses Redis pool. It takes connection per repository action. But it doesn't return to the pool, so it constantly generates new connections.
Sentry Issue: CHRLY-H
*errors.errorString: runtime error: index out of range [1] with length 1
File "/home/travis/.gimme/versions/go1.14.linux.amd64/src/runtime/panic.go", line 88, in goPanicIndex
File "/home/travis/gopath/src/github.com/elyby/chrly/db/redis/redis.go", line 207, in findMojangUuidByUsername
File "/home/travis/gopath/src/github.com/elyby/chrly/db/redis/redis.go", line 195, in GetUuid
File "/home/travis/gopath/src/github.com/elyby/chrly/mojangtextures/storage.go", line 40, in GetUuid
File "/home/travis/gopath/src/github.com/elyby/chrly/mojangtextures/mojang_textures.go", line 171, in getUuidFromCache
...
(10 additional frame(s) were not displayed)
runtime error: index out of range [1] with length 1
Due to the decrease of the limit on the number of nicknames for which you can get uuid from 100 to 10, as well as the decrease in requests rate limit from 600 to ~240 per 10 minutes (gained experimentally), we can't now process all incoming traffic from one server.
During the initial development we had some doubts that we would be able to process the whole load from 1 server, so we thought over the options to solve this problem. There are 2 ideas:
The first option can work in case of placement on a dedicated server with an additional network card, but it's not suitable for some minimal hardware, which we focus on.
The second option is much more viable, because it allows us to share the load on many cheap servers, each of which will work from its own IP address.
The easiest implementation involves launching a web server that will send all incoming requests to the queue and return them as they are returned by the Mojang API. The master node will receive a config parameter that defines the list of IP addresses of the workers to which requests will be sent according to a round-robin algorithm.
But even in the simplest implementation there is a question of the need to ensure the work of the cluster because some of the workers may go down and will have to be temporarily removed from the pool of workers. This task can be solved on our side (which requires additional development) or using some middleware (for example, HAProxy). There is no final solution yet.
Other players who aren't ely.by users cannot see your skin, I have installed the server plugin just so it would allow everyone to see each other but it seems to not function at all.
I have tried using it on the backend servers only (Paper)
and the proxy server (Velocity with Snap! BungeeCord to Velocity plugin translator)
I have also gave the plugin an auth key.
docker-entrypoint.sh
and run the program directly.Right now it's not possible to remove skin without removing a whole user profile. But we should respond with a profile with empty textures when the profile at least exists. We can implement it there, but in the case when the profile has been requested with signed textures, it will be not possible to sign them in place.
So the solution will be to allow the upload of user information without skin and correctly handle it when forming responses.
During working the server may consume more than 1G of memory and not free it, which is definitely not the expected behavior.
The current library is deprecated.
Right now migration is not possible due to the next issue mediocregopher/radix#53.
Sentry Issue: CHRLY-D
Stacktrace (most recent call first):
File "/home/travis/.gimme/versions/go1.14.linux.amd64/src/reflect/value.go", line 460, in call
File "/home/travis/.gimme/versions/go1.14.linux.amd64/src/reflect/value.go", line 321, in Call
File "/home/travis/gopath/src/github.com/elyby/chrly/vendor/github.com/asaskevich/EventBus/event_bus.go", line 158, in doPublish
File "/home/travis/gopath/src/github.com/elyby/chrly/vendor/github.com/asaskevich/EventBus/event_bus.go", line 144, in Publish
File "/home/travis/gopath/src/github.com/elyby/chrly/dispatcher/dispatcher.go", line 27, in Emit
File "/home/travis/gopath/src/github.com/elyby/chrly/mojangtextures/mojang_textures.go", line 196, in getTextures
File "/home/travis/gopath/src/github.com/elyby/chrly/mojangtextures/mojang_textures.go", line 157, in getResult
File "/home/travis/gopath/src/github.com/elyby/chrly/mojangtextures/mojang_textures.go", line 132, in getResultAndBroadcast
textures: Unexpected Mojang response error: 200: Empty Response
The h2non/gock
has already renamed it’s import path from "github.com/h2non/gock" to "gopkg.in/h2non/gock.v1".
As README of h2non/gock
v1.0.14 said, downstream repos should use "gopkg.in/h2non/gock.v1" to get or import h2non/gock
.
Installation
> go get -u gopkg.in/h2non/gock.v1
Examples
See examples directory for more featured use cases.
Simple mocking via tests
package test
import (
"github.com/nbio/st"
"gopkg.in/h2non/gock.v1"
"io/ioutil"
"net/http"
"testing"
)
…
But elyby/chrly
still used the old path:
https://github.com/elyby/chrly/blob/master/Gopkg.lock#L92
[[projects]]
digest = "1:5eeb4bfc6db411dbb34a6d9e5d49a9956b160d59fd004ee8f03fe53c9605c082"
name = "github.com/h2non/gock"
packages = ["."]
pruneopts = ""
revision = "ba88c4862a27596539531ce469478a91bc5a0511"
version = "v1.0.14"
When you use the old path "github.com/h2non/gock" to import the h2non/gock
, it will be very easy to reintroduce h2non/gock
through the import statements "import gopkg.in/h2non/gock.v1" in the go source file of h2non/gock
.
https://github.com/h2non/gock/blob/v1.0.14/_examples/custom_matcher/matcher.go#L5
package main
import (
"fmt"
"gopkg.in/h2non/gock.v1"
"net/http"
)
…
The "gopkg.in/h2non/gock.v1" and "github.com/h2non/gock" are the same repos. This will work in isolation, bring about potential risks and problems.
Replace all the old import paths, change "github.com/h2non/gock"
to "gopkg.in/h2non/gock.v1"
.
Where did you import it: https://github.com/elyby/chrly/search?q=github.com%2Fh2non%2Fgock&unscoped_q=github.com%2Fh2non%2Fgock
This strategy will allow for a faster return of results in case of load bursts. The idea is as follows: the strategy will send a request as soon as there are n
elements in the queue or until t
time has passed since the first element appeared for processing.
Explaining for humans: people from the CIS should easily understand this logic by drawing an analogy with the drivers of the route: until a full car is filled, it will not go 😅
~/g/s/g/e/chrly> dep status
dep: WARNING: branch, version, revision, or source should be provided for "github.com/mediocregopher/radix.v2"
dep: WARNING: branch, version, revision, or source should be provided for "github.com/mono83/slf"
dep: WARNING: branch, version, revision, or source should be provided for "github.com/spf13/viper"
dep: WARNING: branch, version, revision, or source should be provided for "github.com/getsentry/raven-go"
Since Mojang disabled skins.minecraft.net API, Chrly doesn't proxy skins if skin not found in data storage.
We investigated current rate limits for Mojang API. From documentation we know that all public APIs are rate limited and currently set at 600 requests per 10 minutes. And it's true for the biggest part of endpoints, but not for UUID -> Profile + Skin/Cape endpoint. For this point there such note as "This has a much stricter rate limit: You can request the same profile once per minute, however you can send as many unique requests as you like". And it's confirmed: we easily performed 19k requests from the one IP for about a minute (Mojang, if it affects you, we are really sorry. You know, it's all for science!).
So here is our new solution.
Chrly must organize some processing queue. In this queue Chrly will send request to Playernames -> UUIDs endpoint to exchange usernames to actual uuids. These requests shouldn't be triggered more than once per second and contain more than 100 nicknames. After uuids received, we can exchange them to textures via UUID -> Profile + Skin/Cape endpoint. This request can be performed without any queue: just as fast as possible.
Data obviously should be cached, but there appear some questions:
Should we cache username
-> textures
and always respond it or should we cache username
-> uuid
and request textures every time to immediately handle textures changes? It's important, 'cause if we will have rps over 100 (at daytime (UTC+3:00) Ely.by have a way more rps), than we will be unable to check all textures and some users will don't have skins.
Another question is should we use primary storage (Redis) or cache it in the Chrly memory with fixed size and some sort of strategy of repression? Currently, Redis eats about 1.5Gb of memory on our production server and is not going to reduce appetites.
Third question is how long we should cache it? Should it be hour or a few days?
If someone has any ideas about this stuff, I'd like to hear your opinion.
Hi, and first thank you for this convenient service.
I have a proposal: versions before 1.8 use 64x32 skins and expect the API endpoint to return 64x32 texture, failing to properly render it in case a 64x64 skin is returned.
To address that, I suggest cropping images before serving them to the client. This is as simple as just removing bottom 64x32 part of the image, additionally it can be possible to fix slim texture by simply mirroring one pixel wide strip on the arms so it doesn't cause black areas on the skin.
If you approve the idea, I'm ready to work on the necessary changes and submit a PR.
I need a way to somehow notify HAProxy that the server is receiving 429 errors and the server needs to rest.
hash
field is already removed from /textures
response because the game doesn't use it and calculates hash by getting filename from the textures link.
Due to the latest changes in the Mojang's API, we're now able to query only 10 playernames per request rather than 100, as was previously.
We need to adjust this value in the code.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.