{"id":16218195,"url":"https://github.com/quasilyte/ebitengine-resource","last_synced_at":"2025-10-25T06:44:25.041Z","repository":{"id":65661826,"uuid":"595189236","full_name":"quasilyte/ebitengine-resource","owner":"quasilyte","description":"A resource manager for Ebitengine","archived":false,"fork":false,"pushed_at":"2025-05-10T10:36:28.000Z","size":58,"stargazers_count":21,"open_issues_count":3,"forks_count":1,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-05-10T11:28:51.643Z","etag":null,"topics":["ebiten","ebitengine","gamedev","go","golang","loader","resource-loader","resource-manager","resources"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/quasilyte.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-01-30T15:27:30.000Z","updated_at":"2025-05-10T10:36:32.000Z","dependencies_parsed_at":"2023-02-17T21:31:04.564Z","dependency_job_id":"4a976c16-527e-452d-8f61-9888964b8864","html_url":"https://github.com/quasilyte/ebitengine-resource","commit_stats":{"total_commits":21,"total_committers":1,"mean_commits":21.0,"dds":0.0,"last_synced_commit":"f1fc00a87be03d0ba79f56d1c9b5aca855650805"},"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/quasilyte/ebitengine-resource","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/quasilyte%2Febitengine-resource","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/quasilyte%2Febitengine-resource/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/quasilyte%2Febitengine-resource/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/quasilyte%2Febitengine-resource/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/quasilyte","download_url":"https://codeload.github.com/quasilyte/ebitengine-resource/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/quasilyte%2Febitengine-resource/sbom","scorecard":{"id":755418,"data":{"date":"2025-08-11","repo":{"name":"github.com/quasilyte/ebitengine-resource","commit":"c1afcd40eb1f9c7d1c177a6efca894c9cef00bae"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3.1,"checks":[{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Code-Review","score":0,"reason":"Found 0/23 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/go.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/go.yml:15: update your workflow using https://app.stepsecurity.io/secureworkflow/quasilyte/ebitengine-resource/go.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/go.yml:24: update your workflow using https://app.stepsecurity.io/secureworkflow/quasilyte/ebitengine-resource/go.yml/master?enable=pin","Info:   0 out of   2 GitHub-owned GitHubAction dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 6 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Vulnerabilities","score":6,"reason":"4 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GO-2023-1572 / GHSA-qgc7-mgm3-q253","Warn: Project is vulnerable to: GO-2023-1990 / GHSA-j3p8-6mrq-6g7h","Warn: Project is vulnerable to: GO-2023-1989 / GHSA-x92r-3vfx-4cv3","Warn: Project is vulnerable to: GO-2024-2937 / GHSA-9phm-fm57-rhg8"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-22T21:33:39.723Z","repository_id":65661826,"created_at":"2025-08-22T21:33:39.723Z","updated_at":"2025-08-22T21:33:39.723Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":280917405,"owners_count":26413206,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-25T02:00:06.499Z","response_time":81,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ebiten","ebitengine","gamedev","go","golang","loader","resource-loader","resource-manager","resources"],"created_at":"2024-10-10T11:48:50.466Z","updated_at":"2025-10-25T06:44:25.023Z","avatar_url":"https://github.com/quasilyte.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"## Ebitengine Resource Manager Library\n\n![Build Status](https://github.com/quasilyte/ebitengine-resource/workflows/Go/badge.svg)\n[![PkgGoDev](https://pkg.go.dev/badge/mod/github.com/quasilyte/ebitengine-resource)](https://pkg.go.dev/mod/github.com/quasilyte/ebitengine-resource)\n\n### Overview\n\nA resource manager (loader) for [Ebitengine](https://github.com/hajimehoshi/ebiten).\n\n**Key features:**\n\n* Resource caching (only the first load decodes the resource)\n* Easy to use and opinionated\n* iota-friendly typed constants API\n* Int-based keys are also efficient, so the lookups are very fast\n\n**Dependencies:**\n\n* [Ebitengine](https://github.com/hajimehoshi/ebiten) itself\n* [golang.org/x/image](https://pkg.go.dev/golang.org/x/image) for `golang.org/x/image/font`\n\nSome games that were built with this library:\n\n* [Roboden](https://quasilyte.itch.io/roboden)\n* [Cavebots](https://quasilyte.itch.io/cavebots)\n* [Assemblox](https://quasilyte.itch.io/assemblox)\n* [Decipherism](https://quasilyte.itch.io/decipherism)\n* [Retrowave City](https://quasilyte.itch.io/retrowave-city)\n* [Autotanks](https://quasilyte.itch.io/autotanks)\n* [Sinecord](https://quasilyte.itch.io/sinecord)\n* [Learn Georgian](https://quasilyte.itch.io/georgian-trainer)\n\n### Installation\n\n```bash\ngo get github.com/quasilyte/ebitengine-resource\n```\n\n### Quick Start\n\n```go\npackage main\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"strings\"\n\n\t\"github.com/hajimehoshi/ebiten/v2/audio\"\n\tresource \"github.com/quasilyte/ebitengine-resource\"\n)\n\n// The essential game data resources could be enumerated using iota constants.\n// Dynamic game content requires dynamically generated IDs during the run time.\nconst (\n\trawNone resource.RawID = iota\n\trawLevel1Data\n\trawLevel2Data\n\trawDefaultConfig\n)\n\nconst (\n\taudioNone resource.AudioID = iota\n\taudioExample\n)\n\nfunc main() {\n\taudioContext := audio.NewContext(44100)\n\n\tl := resource.NewLoader(audioContext)\n\n\tl.OpenAssetFunc = func(path string) io.ReadCloser {\n\t\treturn io.NopCloser(bytes.NewReader(resdata[path]))\n\t}\n\n\t// Before any resource is loadable, they should be bound.\n\t// You bind (register) resources by using typed registries.\n\t// For instance, RawRegistry is used to register Raw resources.\n\trawResources := map[resource.RawID]resource.RawInfo{\n\t\trawLevel1Data:    {Path: \"maps/level1.json\"},\n\t\trawLevel2Data:    {Path: \"maps/level2.json\"},\n\t\trawDefaultConfig: {Path: \"config.txt\"},\n\t}\n\tl.RawRegistry.Assign(rawResources)\n\tl.AudioRegistry.Assign(map[resource.AudioID]resource.AudioInfo{\n\t\taudioExample: {Path: \"audio/example.wav\", Volume: -0.2},\n\t})\n\n\t// It's possible to preload the resources.\n\t// Just load them once during the load screen or game initialization.\n\t// The second Load for the same resource would return a cached result.\n\tfor id := range rawResources {\n\t\tl.LoadRaw(id)\n\t}\n\n\t// Raw resources are stored as bytes.\n\tvar level1 map[string]any\n\tif err := json.Unmarshal(l.LoadRaw(rawLevel1Data).Data, \u0026level1); err != nil {\n\t\tpanic(err)\n\t}\n\tfmt.Println(level1[\"name\"]) // Prints \"level1\"\n\n\t// Now let's try using audio resources.\n\t// Audio resources wrap the sound into an *audio.Player\n\t// that is ready to be used. Every AudioID has its own audio player.\n\t// Most of the time, if you want to play a sound, you need\n\t// to rewind the player before doing that.\n\ta := l.LoadWAV(audioExample)\n\tif err := a.Player.Rewind(); err != nil {\n\t\tpanic(err)\n\t}\n\ta.Player.Play()\n}\n\n// This is our stub for the real data.\n// In reality, you would probably use a combination of\n// go:embed store and real filesystem.\nvar resdata = map[string][]byte{\n\t\"maps/level1.json\": []byte(`{\"name\": \"level1\"}`),\n\t\"maps/level2.json\": []byte(`{\"name\": \"level2\"}`),\n\t\"config.txt\":       []byte(\"some example config\\n\"),\n\n\t// Some minimal-size valid wav resource.\n\t\"audio/example.wav\": []byte(strings.Join([]string{\n\t\t\"\\x52\\x49\\x46\\x46\\x24\\x00\\x00\\x00\\x57\\x41\\x56\\x45\\x66\\x6d\\x74\",\n\t\t\"\\x20\\x10\\x00\\x00\\x00\\x01\\x00\\x01\\x00\\x44\\xac\\x00\\x00\\x88\\x58\",\n\t\t\"\\x01\\x00\\x02\\x00\\x10\\x00\\x64\\x61\\x74\\x61\\x00\\x00\\x00\\x00\",\n\t}, \"\")),\n}\n```\n\n### Introduction\n\nHow to use this library properly?\n\nYou start by creating a loader with [resource.NewLoader()](https://pkg.go.dev/github.com/quasilyte/ebitengine-resource#NewLoader). It should happen after you acquired an `*audio.Context` from Ebitengine. It's not recommended to make loader global, pass it as an explicit dependency everywhere you need to access the game resources.\n\nThen you bind the resources using typed registries. For instance, binding image resources is done via `loader.ImageRegistry` field (use `Set` or `Assign` methods).\n\nThe loader acts as a cached resource access point. Resources are keyed by their ID. The ID is a simple integer. All metadata is associated with that ID too. It's recommended to make the core resources iota-style constants.\n\nIf you want to preload a resource, do a respective `Load` (e.g. [LoadImage](https://pkg.go.dev/github.com/quasilyte/ebitengine-resource#Loader.LoadImage), [LoadAudio](https://pkg.go.dev/github.com/quasilyte/ebitengine-resource#Loader.LoadAudio)) call either during a game launch or during the loading screen.\n\nMost types in a package can be described by these categories:\n\n1. ID types that belong to specific kind of resource (e.g. [ImageID](https://pkg.go.dev/github.com/quasilyte/ebitengine-resource#ImageID), [AudioID](https://pkg.go.dev/github.com/quasilyte/ebitengine-resource#AudioID))\n2. Info objects that describe the resource (e.g. [ImageInfo](https://pkg.go.dev/github.com/quasilyte/ebitengine-resource#ImageInfo), [AudioInfo](https://pkg.go.dev/github.com/quasilyte/ebitengine-resource#AudioInfo))\n3. The actual resource objects (e.g. [Image](https://pkg.go.dev/github.com/quasilyte/ebitengine-resource#Image), [Audio](https://pkg.go.dev/github.com/quasilyte/ebitengine-resource#Audio))\n\nThe info objects should be bound before the resource is accessed via `Load` method. It's possible to bind extra resources during the run-time.\n\nSupported resource kinds:\n\n* [Audio](https://pkg.go.dev/github.com/quasilyte/ebitengine-resource#Audio) (`*Audio.Player` with decoded stream)\n* [Font](https://pkg.go.dev/github.com/quasilyte/ebitengine-resource#Font) ([font.Face](https://pkg.go.dev/golang.org/x/image/font#Face) with relevant properties like font size and line spacing)\n* [Image](https://pkg.go.dev/github.com/quasilyte/ebitengine-resource#Image) (`*ebiten.Image` created from a texture)\n* [Shader](https://pkg.go.dev/github.com/quasilyte/ebitengine-resource#Shader) (a compiled `*ebiten.Shader`)\n* [Raw](https://pkg.go.dev/github.com/quasilyte/ebitengine-resource#Raw) (stored as `[]byte`)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fquasilyte%2Febitengine-resource","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fquasilyte%2Febitengine-resource","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fquasilyte%2Febitengine-resource/lists"}