Remplissage du dépôt

This commit is contained in:
sys4-fr 2018-12-13 21:25:14 +01:00
commit 3ba9c542bb
10 changed files with 706 additions and 0 deletions

6
.hg_archival.txt Normal file
View File

@ -0,0 +1,6 @@
repo: 09eecdf0f6536402038ec3167b37def72e380b40
node: 9957de6f53674924478b884100a42b8dec833532
branch: default
latesttag: null
latesttagdistance: 29
changessincelatesttag: 29

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2015 Sascha L. Teichmann
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.

54
README.md Normal file
View File

@ -0,0 +1,54 @@
# MTAutocolors
A tool to generate a colors.txt from the registered nodes of a Minetest server.
To have a colors.txt for Minetest mapping for example with [MTSatellite](https://bitbucket.org/s_l_teichmann/mtsatellite)
you need a colors.txt file. You can grab one from [VanessaE](http://digitalaudioconcepts.com/vanessa/hobbies/minetest/colors.txt)
or build one yourself.
gravgun wrote the generator [automappercolors](https://github.com/MinetestForFun/minetest-minetestforfun-server/tree/master/mods/automappercolors).
MTAutocolors is a slightly extended version of this generator being much faster, too (The image processor
is written in Go instead of Python). It consists of two parts:
1. The included [mod](https://bitbucket.org/s_l_teichmann/mtautocolors/src/default/mods/automappercolors/) to be installed into your minetest
server to extract the needed node informations.
2. The program `mtautocolors` to generate the final colors.txt file from the
informations gathered by the mod.
## Build
Your need a [Go](http://golang.org/) 1.8 or later [installation](https://golang.org/dl/) to compile the program.
Go 1.9.2+ is recommended as there is a known issue with decoding images in earlier versions.
Mercurial and Git have to be install installed.
$ go get -u bitbucket.org/s_l_teichmann/mtautocolors/cmd/mtautocolors
will result in a binary `mtautocolors` to be put into your PATH.
## Usage
Install the [mod](https://bitbucket.org/s_l_teichmann/mtautocolors/src/default/mods/automappercolors/) into your
Minetest server. Start your game. Now you can type `/amcdumpnodes` as a chat command in the Minetest client. This
will result in a file `amc_nodes.txt` in the data directory of your world. Once created you can deactivate the
mod until you install or remove mods or update the game in a way which changes the registered nodes.
`mtautocolors` needs a few arguments. The path to the `amc_nodes.txt` file generated with the mod and
paths to the images of your minetest server. This would be the root of the minetest server and/or the folder
where you have the actual minetest game. Something like this (adjusted to your setting):
$ mtautocolors -predefined=/predefined.json \
/home/xyz/minetest/worlds/colors/amc_nodes.txt \
/home/xyz/minetest/ /home/xyz/minetest_game > colors.txt
There is an option to overwrite the machine generated behavior for certain kinds
of nodes with a JSON file. Look at [predefined.json](https://bitbucket.org/s_l_teichmann/mtautocolors/src/default/predefined.json)
to get the idea. Basically its a list of entries with a regular expression matching the node name
and a tuple of RGBA values which should be used instead of the machine generated.
Transparency: Some nodes should be transparent like glass or water. `mtautocolors` only generates
a alpha channel for nodes with of certain draw types. By default this are the ones which drawtype contains 'glasslike' or 'liquid'.
This can be overwritten with the `-transparent=` flag.
This is Free Software under the terms of the MIT license. See [LICENSE](https://bitbucket.org/s_l_teichmann/mtautocolors/src/default/LICENSE)
for details.
gravgun's original is covered by the WTFPL license. See [LICENSE](https://bitbucket.org/s_l_teichmann/mtautocolors/src/default/mods/automappercolors/LICENSE)
for details, too.

View File

@ -0,0 +1,87 @@
// Copyright 2015 by Sascha L. Teichmann
// Use of this source code is governed by the MIT license
// that can be found in the LICENSE file.
package main
import (
"bufio"
"fmt"
"image"
"image/color"
"image/draw"
"os"
_ "image/jpeg"
_ "image/png"
)
func loadRGBA(filename string) (*image.RGBA, error) {
file, err := os.Open(filename)
if err != nil {
return nil, err
}
defer file.Close()
var img image.Image
if img, _, err = image.Decode(bufio.NewReader(file)); err != nil {
return nil, fmt.Errorf("Decoding '%s' failed: %s", filename, err)
}
if rgba, ok := img.(*image.RGBA); ok {
return rgba, nil
}
bounds := img.Bounds()
rgba := image.NewRGBA(bounds)
draw.Draw(rgba, bounds, img, image.ZP, draw.Src)
return rgba, nil
}
func averageColor(filename string) (color.Color, error) {
img, err := loadRGBA(filename)
if err != nil {
return nil, err
}
bounds := img.Bounds()
if bounds.Empty() {
return color.Black, nil
}
y := img.PixOffset(bounds.Min.X, bounds.Min.Y)
yEnd := img.PixOffset(bounds.Min.X, bounds.Max.Y)
w := bounds.Dx() * 4
pix := img.Pix
var r, g, b, a uint64
for ; y < yEnd; y += img.Stride {
for pos, end := y, y+w; pos < end; pos += 4 {
pa := uint64(pix[pos+3])
r += pa * uint64(pix[pos])
g += pa * uint64(pix[pos+1])
b += pa * uint64(pix[pos+2])
a += pa
}
}
r /= 255
g /= 255
b /= 255
if s := a / 255; s > 0 {
r /= s
g /= s
b /= s
}
col := color.RGBA{
R: uint8(r),
G: uint8(g),
B: uint8(b),
A: uint8(a / uint64(bounds.Dx()*bounds.Dy()))}
return &col, nil
}

237
cmd/mtautocolors/main.go Normal file
View File

@ -0,0 +1,237 @@
// Copyright 2015 by Sascha L. Teichmann
// Use of this source code is governed by the MIT license
// that can be found in the LICENSE file.
package main
import (
"bufio"
"flag"
"fmt"
"image/color"
"io"
"log"
"os"
"path/filepath"
"regexp"
"runtime"
"sort"
"strings"
"sync"
)
var nodesSplit = regexp.MustCompile(" +")
func usage() {
fmt.Fprintf(os.Stderr,
"Usage: %s [<options>] <nodes.txt> [<data source directory> ...]\n",
os.Args[0])
fmt.Fprintln(os.Stderr, "Options:")
flag.PrintDefaults()
}
func main() {
var predef string
var transparent string
var workers int
flag.Usage = usage
flag.StringVar(&predef, "predefined", "", "predefined colors")
flag.StringVar(&predef, "p", "", "predefined colors (shorthand)")
flag.StringVar(&transparent, "transparent", "glasslike,liquid", "transparent nodes")
flag.StringVar(&transparent, "t", "glasslike,liquid", "transparent nodes (shorthand)")
flag.IntVar(&workers, "workers", 0, "number of image processing workers")
flag.IntVar(&workers, "w", 0, "number of image processing workers (shorthand)")
flag.Parse()
nargs := flag.NArg()
if nargs < 1 {
usage()
os.Exit(1)
}
var err error
var predefs PredefCols
if predef != "" {
if predefs, err = LoadPredefCols(predef); err != nil {
log.Fatalf("Cannot load predefined colors: %s\n", err)
}
}
var roots []string
if nargs > 1 {
roots = flag.Args()[1:]
} else {
roots = []string{"."}
}
files, err := buildFileIndex(roots)
if err != nil {
log.Fatalf("error while building file index: %s\n", err)
}
drawTypes := strings.Split(transparent, ",")
if err = process(flag.Arg(0), drawTypes, files, predefs, workers); err != nil {
log.Fatalf("error while generating colors: %s\n", err)
}
}
func contains(haystack []string, needle string) bool {
for _, straw := range haystack {
if strings.Contains(needle, straw) {
return true
}
}
return false
}
type outLine struct {
name string
col color.Color
alpha bool
}
func asByte(x uint32) byte {
return byte(x >> 8)
}
func (ol *outLine) print(out io.Writer) {
r, g, b, a := ol.col.RGBA()
ba := asByte(a)
if ol.alpha && ba < 255 {
fmt.Fprintf(out, "%s %d %d %d %d\n",
ol.name, asByte(r), asByte(g), asByte(b), ba)
} else {
fmt.Fprintf(out, "%s %d %d %d\n",
ol.name, asByte(r), asByte(g), asByte(b))
}
}
func process(
nodesFile string,
drawTypes []string,
files map[string]string,
predefs PredefCols,
workers int) error {
file, err := os.Open(nodesFile)
if err != nil {
return err
}
defer file.Close()
lineCh := make(chan []string)
if workers < 1 {
workers = runtime.NumCPU()
}
var outLineMu sync.Mutex
var outLines []outLine
var wg sync.WaitGroup
for i := 0; i < workers; i++ {
wg.Add(1)
go func() {
defer wg.Done()
lines:
for line := range lineCh {
name, drawType, texture := line[0], line[1], line[2]
var c color.Color
switch col := predefs.findCol(name); {
case col != nil && col.Complete():
c = col
case col == nil || !col.Complete():
tfile := files[texture]
if tfile == "" {
log.Printf("WARN: node '%s' missing texture '%s'\n", name, texture)
continue lines
}
avg, err := averageColor(tfile)
if err != nil {
log.Printf("WARN: node '%s' defect image: %s\n", name, err)
continue lines
}
if col != nil {
c = col.Apply(avg)
} else {
c = avg
}
}
alpha := contains(drawTypes, drawType)
outLineMu.Lock()
outLines = append(outLines, outLine{
name: name,
col: c,
alpha: alpha,
})
outLineMu.Unlock()
}
}()
}
scanner := bufio.NewScanner(file)
for lineNo := 1; scanner.Scan(); lineNo++ {
parts := nodesSplit.Split(scanner.Text(), 3)
if len(parts) < 3 {
log.Printf("WARN: line %d too short.\n", lineNo)
} else {
lineCh <- parts
}
}
close(lineCh)
wg.Wait()
if err := scanner.Err(); err != nil {
return err
}
// To make it more deterministic.
sort.Slice(outLines, func(i, j int) bool {
return outLines[i].name < outLines[j].name
})
out := bufio.NewWriter(os.Stdout)
for i := range outLines {
outLines[i].print(out)
}
return out.Flush()
}
func buildFileIndex(roots []string) (map[string]string, error) {
index := make(map[string]string)
acceptedExts := map[string]bool{
".png": true,
".jpg": true,
".jpeg": true}
walkFn := func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if !info.Mode().IsRegular() {
return nil
}
name := info.Name()
if !acceptedExts[strings.ToLower(filepath.Ext(name))] {
return nil
}
if _, found := index[name]; !found {
index[name] = path
} else {
log.Printf("WARN: more than one file for '%s'\n", name)
}
return nil
}
for _, root := range roots {
if err := filepath.Walk(root, walkFn); err != nil {
return nil, err
}
}
return index, nil
}

View File

@ -0,0 +1,116 @@
// Copyright 2015 by Sascha L. Teichmann
// Use of this source code is governed by the MIT license
// that can be found in the LICENSE file.
package main
import (
"bufio"
"encoding/json"
"image/color"
"os"
"regexp"
"strconv"
)
type Component struct {
Value uint8
Used bool
}
type Expr struct{ *regexp.Regexp }
type Col struct {
E Expr `json:"expr"`
R Component `json:"r"`
G Component `json:"g"`
B Component `json:"b"`
A Component `json:"a"`
}
type PredefCols []Col
func (e *Expr) UnmarshalJSON(data []byte) error {
unquoted := string(data[1 : len(data)-1])
expr, err := regexp.Compile(unquoted)
if err != nil {
return err
}
*e = Expr{expr}
return nil
}
func (c *Component) UnmarshalJSON(data []byte) error {
v, err := strconv.ParseUint(string(data), 10, 8)
if err != nil {
return err
}
c.Value = uint8(v)
c.Used = true
return nil
}
func LoadPredefCols(filename string) (PredefCols, error) {
file, err := os.Open(filename)
if err != nil {
return nil, err
}
defer file.Close()
decoder := json.NewDecoder(bufio.NewReader(file))
var predef PredefCols
if err = decoder.Decode(&predef); err != nil {
return nil, err
}
return predef, nil
}
func (pd *PredefCols) findCol(name string) *Col {
for i, n := 0, len(*pd); i < n; i++ {
if (*pd)[i].E.MatchString(name) {
return &(*pd)[i]
}
}
return nil
}
func (c *Col) Complete() bool {
return c.R.Used && c.G.Used && c.B.Used && c.A.Used
}
func (c *Col) RGBA() (r, g, b, a uint32) {
r = uint32(c.R.Value)
r |= r << 8
g = uint32(c.G.Value)
g |= g << 8
b = uint32(c.B.Value)
b |= b << 8
a = uint32(c.A.Value)
a |= a << 8
return
}
func (c *Col) Apply(other color.Color) color.Color {
r, g, b, a := other.RGBA()
x := color.RGBA{
R: uint8(r >> 8),
G: uint8(g >> 8),
B: uint8(b >> 8),
A: uint8(a >> 8)}
if c.R.Used {
x.R = c.R.Value
}
if c.G.Used {
x.G = c.G.Value
}
if c.B.Used {
x.B = c.B.Value
}
if c.A.Used {
x.A = c.A.Value
}
return &x
}

View File

@ -0,0 +1,16 @@
[
{ "expr": "^default:(river_water_.+|water.+)", "a": 128},
{ "expr": "^default:glass", "a": 128},
{ "expr": "^pumpkin_face_light", "r": 255, "g": 255, "b": 36, "a": 255},
{ "expr": "^default:torch", "r": 255, "g": 255, "b": 0, "a": 255},
{ "expr": "^farming:cotton_(6|7|8)", "r": 255, "g": 255, "b": 255},
{ "expr": "^default:junglewood", "r": 79, "g": 57, "b": 26, "a": 255},
{ "expr": "^default:(desertstone|desert_stone)", "r": 149, "g": 99, "b": 80, "a": 255},
{ "expr": "^pipeworks:tube_.+", "r": 190, "g": 190, "b": 190, "a": 255},
{ "expr": "^pipeworks:(priority|crossing|detector)_tube_.+", "r": 190, "g": 190, "b": 190, "a": 255},
{ "expr": "^tnt:gunpowder_burning$", "r": 122, "g": 59, "b": 1, "a": 255},
{ "expr": "^tnt:gunpowder$", "r": 0, "g": 0, "b": 0 , "a": 255},
{ "expr": "^default:nyancat_rainbow$", "r": 102, "g": 50, "b": 255, "a": 255},
{ "expr": "^default:nyancat$", "r": 255, "g": 153, "b": 255, "a": 255 },
{ "expr": "^default:mossycobble", "r": 75, "g": 100, "b": 71, "a": 255 }
]

View File

@ -0,0 +1,13 @@
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
Version 2, December 2004
Copyright (C) 2015 gravgun
14 rue de Plaisance, 75014 Paris, France
Everyone is permitted to copy and distribute verbatim or modified
copies of this license document, and changing it is allowed as long
as the name is changed.
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. You just DO WHAT THE FUCK YOU WANT TO.

View File

@ -0,0 +1,50 @@
-- Automappercolors by gravgun, modified by s-l-teichmann
-- WTFPL
function amc_dumpnodes()
local fd, err = io.open(minetest.get_worldpath()..'/amc_nodes.txt', 'wb')
if not fd then
return 0, err
end
local n = 0
for name, def in pairs(minetest.registered_nodes) do
if def.drawtype ~= 'airlike' then
local tile = def.tiles or def.tile_images
if type(tile) == 'table' then
tile = tile[1]
if type(tile) == 'table' then
tile = tile.name
end
end
if tile ~= nil then
tile = (tile .. '^'):match('([a-zA-Z0-9\\._-]-)^')
fd:write(name .. ' ' .. def.drawtype .. ' ' .. tile .. '\n')
n = n + 1
end
end
end
fd:close()
return n, "done"
end
minetest.register_chatcommand("amcdumpnodes", {
params = "",
description = "",
func = function(plname, param)
local n, msg = amc_dumpnodes()
if n == 0 then
minetest.chat_send_player(plname, 'io.open: ' .. msg)
else
minetest.chat_send_player(plname, n .. " nodes dumped.")
end
end,
})
minetest.after(1, function(args)
amc_dumpnodes()
if minetest.setting_getbool("log_mods") then
minetest.log("action", "[automappercolors] nodes dumped")
end
end)

106
predefined.json Normal file
View File

@ -0,0 +1,106 @@
[
{
"expr": "^default:([a-z_]*)glass",
"a": 64
},
{
"expr": "^default:torch",
"r": 255,
"g": 255,
"b": 0,
"a": 255
},
{
"expr": "^default:ice",
"r": 74,
"g": 105,
"b": 159,
"a": 159
},
{
"expr": "^default:water_([a-z]+)",
"r": 43,
"g": 97,
"b": 183,
"a": 128
},
{
"expr": "^default:dirt_with_grass",
"r": 107,
"g": 134,
"b": 51,
"a": 255
},
{
"expr": "^flowers:cotton_plant",
"r": 199,
"g": 218,
"b": 158
},
{
"expr": "^flowers:seaweed",
"r": 48,
"g": 114,
"b": 107
},
{
"expr": "^flowers:waterlily",
"r": 119,
"g": 166,
"b": 100
},
{
"expr": "^flowers:waterlily_225",
"r": 119,
"g": 166,
"b": 100
},
{
"expr": "^flowers:waterlily_45",
"r": 119,
"g": 166,
"b": 100
},
{
"expr": "^flowers:waterlily_675",
"r": 119,
"g": 166,
"b": 100
},
{
"expr": "^flowers:dandelion_white",
"r": 161,
"g": 174,
"b": 149
},
{
"expr": "^flowers:dandelion_yellow",
"r": 144,
"g": 138,
"b": 0
},
{
"expr": "^flowers:geranium",
"r": 75,
"g": 101,
"b": 84
},
{
"expr": "^flowers:rose",
"r": 153,
"g": 9,
"b": 0
},
{
"expr": "^flowers:tulip",
"r": 175,
"g": 114,
"b": 0
},
{
"expr": "^flowers:viola",
"r": 84,
"g": 90,
"b": 64
}
]