{"id":18809909,"url":"https://github.com/bytebit-org/roblox-valueholders","last_synced_at":"2026-01-10T12:30:16.039Z","repository":{"id":57683515,"uuid":"472845658","full_name":"Bytebit-Org/roblox-ValueHolders","owner":"Bytebit-Org","description":"A module for passing any values around by sharing a pointer, as well as allowing consumers to subscribe to changes and allowing authors to hold locks on changing the value.","archived":false,"fork":false,"pushed_at":"2022-04-05T05:19:03.000Z","size":353,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-01-30T03:18:26.575Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","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/Bytebit-Org.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-03-22T16:29:03.000Z","updated_at":"2022-03-22T18:45:10.000Z","dependencies_parsed_at":"2022-09-05T17:31:04.654Z","dependency_job_id":null,"html_url":"https://github.com/Bytebit-Org/roblox-ValueHolders","commit_stats":null,"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Bytebit-Org%2Froblox-ValueHolders","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Bytebit-Org%2Froblox-ValueHolders/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Bytebit-Org%2Froblox-ValueHolders/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Bytebit-Org%2Froblox-ValueHolders/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Bytebit-Org","download_url":"https://codeload.github.com/Bytebit-Org/roblox-ValueHolders/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":239743908,"owners_count":19689563,"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","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":[],"created_at":"2024-11-07T23:18:09.314Z","updated_at":"2026-01-10T12:30:15.969Z","avatar_url":"https://github.com/Bytebit-Org.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Value Holders\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://github.com/Bytebit-Org/roblox-ValueHolders/actions\"\u003e\n      \u003cimg src=\"https://github.com/Bytebit-Org/roblox-ValueHolders/workflows/CI/badge.svg\" alt=\"CI status\" /\u003e\n  \u003c/a\u003e\n  \u003ca href=\"http://makeapullrequest.com\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/PRs-welcome-blue.svg\" alt=\"PRs Welcome\" /\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://opensource.org/licenses/MIT\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/License-MIT-blue.svg\" alt=\"License: MIT\" /\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://discord.gg/QEz3v8y\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/discord-join-7289DA.svg?logo=discord\u0026longCache=true\u0026style=flat\" alt=\"Discord server\" /\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\nValue Holders is a module for passing any values around by sharing a pointer, as well as allowing consumers to subscribe to changes and allowing authors to hold locks on changing the value.\n\n## Installation\n### roblox-ts\nSimply install to your [roblox-ts](https://roblox-ts.com/) project as follows:\n```\nnpm i @rbxts/value-holders\n```\n\n### Wally\n[Wally](https://github.com/UpliftGames/wally/) users can install this package by adding the following line to their `Wally.toml` under `[dependencies]`:\n```\nValueHolders = \"bytebit/value-holders@1.0.7\"\n```\n\nThen just run `wally install`.\n\n### From model file\nModel files are uploaded to every release as `.rbxmx` files. You can download the file from the [Releases page](https://github.com/Bytebit-Org/roblox-ValueHolders/releases) and load it into your project however you see fit.\n\n### From model asset\nNew versions of the asset are uploaded with every release. The asset can be added to your Roblox Inventory and then inserted into your Place via Toolbox by getting it [here.](https://www.roblox.com/library/9170323611/Value-Holders-Package)\n\n## Documentation\nDocumentation can be found [here](https://github.com/Bytebit-Org/roblox-ValueHolders/tree/master/docs), is included in the TypeScript files directly, and was generated using [TypeDoc](https://typedoc.org/).\n\n## Examples\n### Standard IValueHolder and a Readonly consumer\nHere's an example of using a standard `IValueHolder` with one class, `Writer`, that contains the value and exposes it publicly as an `IReadonlyValueHolder` and one class, `Reader`, that only reads from the `IValueHolder` by using the `IReadonlyValueHolder` reference (in the TypeScript example). The actual value isn't very important for the purposes of this example, so we'll just have `Writer` update the value once every second.\n\n\u003cdetails\u003e\n  \u003csummary\u003eroblox-ts example\u003c/summary\u003e\n\n  ```ts\n  import { IValueHolder, IReadonlyValueHolder, ValueHolder } from \"@rbxts/value-holders\";\n\n  export class Writer {\n    public readonly valueHolder: IReadonlyValueHolder\u003cnumber\u003e;\n\n    private readonly internalValueHolder: IValueHolder\u003cnumber\u003e;\n\n    public constructor() {\n      this.internalValueHolder = new ValueHolder(0);\n      this.valueHolder = this.internalValueHolder;\n\n      this.incrementValueEverySecond();\n    }\n\n    private incrementValueEverySecond() {\n      while (true) {\n        task.wait(1);\n        valueHolder.updateValue(currentValue =\u003e currentValue + 1);\n      }\n    }\n  }\n\n  export class Reader {\n    public constructor(writer: Writer) {\n      this.printEveryValueUpdate(writer.valueHolder);\n    }\n\n    private printEveryValueUpdate(valueHolder: IReadonlyValueHolder\u003cnumber\u003e) {\n      valueHolder.valueChanged.Connect(newValue =\u003e print(\"New value is: \" + newValue));\n    }\n  }\n  ```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003eLuau example\u003c/summary\u003e\n\n  ```lua\n  local ValueHolder = require(path.to.modules[\"value-holders\"]).ValueHolder\n\n  local Writer = {}\n  Writer.__index = Writer\n\n  local WriterConstructor = {}\n  function WriterConstructor.new()\n    local self = {}\n    setmetatable(self, Writer)\n\n    self.valueHolder = ValueHolder.create(0)\n    _incrementValueEverySecond(self)\n\n    return self\n  end\n\n  function _incrementValueEverySecond(self)\n    while true do\n      task.wait(1)\n      self.valueHolder:updateValue(function (currentValue)\n        return currentValue + 1\n      end)\n    end\n  end\n\n  local Reader = {}\n  Reader.__index = Reader\n\n  local ReaderConstructor = {}\n  function ReaderConstructor.new(writer)\n    local self = {}\n    setmetatable(self, Reader)\n\n    _printEveryValueUpdate(self, writer.valueHolder)\n\n    return self\n  end\n\n  function _printEveryValueUpdate(self, valueHolder)\n    valueHolder.valueChanged:Connect(function (newValue)\n      print(\"New value is: \", newValue)\n    end)\n  end\n\n  return {\n    Writer = WriterConstructor,\n    Reader = ReaderConstructor\n  }\n  ```\n\u003c/details\u003e\n\n### Two writers fighting over one ILockableValueHolder instance\nThe `ILockableValueHolder` interface is meant to enable some writers to shut off access to further writes from other potential writers. In this example, there will be a class that locks the value holder via a public method and another class that tries to set the value to 0 every second. Whenever the value is locked, the first class will increment the value every second. As in the previous example, we'll also have a Reader that just prints the value every time that it changes.\n\n\u003cdetails\u003e\n  \u003csummary\u003eroblox-ts example\u003c/summary\u003e\n\n  ```ts\n  import { ILockableValueHolder, IReadonlyValueHolder, LockableValueHolder } from \"@rbxts/value-holders\";\n  \n  // not going to use this but just for example purposes\n  const exampleLockableValueHolder = LockableValueHolder.create(0);\n\n  export class LockedIncrementingWriter {\n    private lockKey: object | undefined = undefined;\n\n    public constructor(private readonly lockableValueHolder: ILockableValueHolder) {\n      this.incrementValueEverySecond();\n    }\n\n    public takeLock() {\n      if (this.lockKey !== undefined) {\n        return;\n      }\n\n      this.lockKey = this.lockableValueHolder.tryTakeLock();\n    }\n\n    public releaseLock() {\n      if (this.lockKey !== undefined) {\n        return;\n      }\n\n      this.lockableValueHolder.releaseLock(this.lockKey);\n      this.lockKey = undefined;\n    }\n\n    private incrementValueEverySecond() {\n      while (true) {\n        task.wait(1);\n        if (lockableValueHolder.isLocked()) {\n          lockableValueHolder.updateValue(\n            currentValue =\u003e currentValue + 1,\n            this.lockKey);\n        }\n      }\n    }\n  }\n\n  export class ZeroWriter {\n    public constructor(private readonly lockableValueHolder: ILockableValueHolder) {\n      this.setValueToZeroEverySecond();\n    }\n\n    private setValueToZeroEverySecond() {\n      while (true) {\n        task.wait(1);\n        if (!lockableValueHolder.isLocked()) {\n          // If the code did not first check whether the value holder was locked,\n          // then this line would error\n          lockableValueHolder.setValue(0);\n        }\n      }\n    }\n  }\n\n  export class Reader {\n    public constructor(valueHolder: IReadonlyValueHolder) {\n      this.printEveryValueUpdate(valueHolder);\n    }\n\n    private printEveryValueUpdate(valueHolder: IReadonlyValueHolder\u003cnumber\u003e) {\n      valueHolder.valueChanged.Connect(newValue =\u003e print(\"New value is: \" + newValue));\n    }\n  }\n  ```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003eLuau example\u003c/summary\u003e\n\n  ```lua\n  local LockableValueHolder = require(path.to.modules[\"value-holders\"]).LockableValueHolder\n\n  -- not going to use this but just for example purposes\n  local exampleLockableValueHolder = LockableValueHolder.create(0);\n\n  local LockedIncrementingWriter = {}\n  LockedIncrementingWriter.__index = LockedIncrementingWriter\n\n  local LockedIncrementingWriterConstructor = {}\n  function LockedIncrementingWriterConstructor.new(lockableValueHolder)\n    local self = {}\n    setmetatable(self, LockedIncrementingWriter)\n\n    self._lockableValueHolder = lockableValueHolder\n    _incrementValueEverySecond(self)\n\n    return self\n  end\n\n  function LockedIncrementingWriter:takeLock()\n    if self._lockKey then\n      return\n    end\n\n    self._lockKey = self._lockableValueHolder:tryTakeLock();\n  end\n\n  function LockedIncrementingWriter:releaseLock()\n    if self._lockKey then\n      return\n    end\n\n    self._lockableValueHolder:releaseLock(self._lockKey);\n    self._lockKey = nil;\n  end\n\n  function _incrementValueEverySecond(self)\n    while true do\n      task.wait(1)\n      if self._lockableValueHolder:isLocked() then\n        self._lockableValueHolder:updateValue(function (currentValue)\n          return currentValue + 1\n        end, self._lockKey)\n      end\n    end\n  end\n\n  local ZeroWriter = {}\n  ZeroWriter.__index = ZeroWriter\n\n  local ZeroWriterConstructor = {}\n  function ZeroWriterConstructor.new(lockableValueHolder)\n    local self = {}\n    setmetatable(self, ZeroWriter)\n\n    self._lockableValueHolder = lockableValueHolder\n    _setValueToZeroEverySecond(self)\n\n    return self\n  end\n\n  function _setValueToZeroEverySecond(self)\n    while true do\n      task.wait(1)\n      if not self._lockableValueHolder:isLocked() then\n        -- If the code did not first check whether the value holder was locked,\n        -- then this line would error\n        self._lockableValueHolder:setValue(0)\n      end\n    end\n  end\n\n  local Reader = {}\n  Reader.__index = Reader\n\n  local ReaderConstructor = {}\n  function ReaderConstructor.new(valueHolder)\n    local self = {}\n    setmetatable(self, Reader)\n\n    _printEveryValueUpdate(self, valueHolder)\n\n    return self\n  end\n\n  function _printEveryValueUpdate(self, valueHolder)\n    valueHolder.valueChanged:Connect(function (newValue)\n      print(\"New value is: \", newValue)\n    end)\n  end\n\n  return {\n    LockedIncrementingWriter = LockedIncrementingWriterConstructor,\n    ZeroWriter = ZeroWriterConstructor,\n    Reader = ReaderConstructor\n  }\n  ```\n\u003c/details\u003e","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbytebit-org%2Froblox-valueholders","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbytebit-org%2Froblox-valueholders","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbytebit-org%2Froblox-valueholders/lists"}