code:
<!DOCTYPE html>

    <meta charset="utf-8">
  <link rel="dns-prefetch" href="https://github.githubassets.com">
  <link rel="dns-prefetch" href="https://avatars0.githubusercontent.com">
  <link rel="dns-prefetch" href="https://avatars1.githubusercontent.com">
  <link rel="dns-prefetch" href="https://avatars2.githubusercontent.com">
  <link rel="dns-prefetch" href="https://avatars3.githubusercontent.com">
  <link rel="dns-prefetch" href="https://github-cloud.s3.amazonaws.com">
  <link rel="dns-prefetch" href="https://user-images.githubusercontent.com/">

  <link crossorigin="anonymous" media="all" integrity="sha512-/YEVWs7BzxfKyUd6zVxjEQcXRWsLbcEjv045Rq8DSoipySmQblhVKxlXLva2GtNd5DhwCxHwW1RM0N9I7S2Vew==" rel="stylesheet" href="https://github.githubassets.com/assets/frameworks-481a47a96965f6706fb41bae0d14b09a.css" />
  <link crossorigin="anonymous" media="all" integrity="sha512-xod5+iick6st6a0tvmfiqwCFLFTRzOkGLX0SAqY7SQFl0Q3sWxKUB2rm1Kw4Vz/57FMg7chtfVYeUB/Yr/xmFQ==" rel="stylesheet" href="https://github.githubassets.com/assets/site-d3c48f1b58ea95d9efb184fd4592b411.css" />
    <link crossorigin="anonymous" media="all" integrity="sha512-xlKdcTpXrrd+rLG1h8yYl5yt/S94zWLil75KS5529Mf0/RdApUbGTKcFbY0OZMotEz3l11k0GqfDAhasayWehw==" rel="stylesheet" href="https://github.githubassets.com/assets/github-eabfbaded2e91939e805d1a3af34018a.css" />

  <meta name="viewport" content="width=device-width">

  <title>bweapons_modpack/bweapons_magic_pack_reload.ogg at master · ClockGen/bweapons_modpack · GitHub</title>
    <meta name="description" content="A weapons modpack aiming at providing a complete, technologic and magic progression of ranged weapons, along with an API to easily make your own ones - ClockGen/bweapons_modpack">
    <link rel="search" type="application/opensearchdescription+xml" href="/opensearch.xml" title="GitHub">
  <link rel="fluid-icon" href="https://github.com/fluidicon.png" title="GitHub">
  <meta property="fb:app_id" content="1401488693436528">

    <meta name="twitter:image:src" content="https://avatars0.githubusercontent.com/u/12116711?s=400&v=4" /><meta name="twitter:site" content="@github" /><meta name="twitter:card" content="summary" /><meta name="twitter:title" content="ClockGen/bweapons_modpack" /><meta name="twitter:description" content="A weapons modpack aiming at providing a complete, technologic and magic progression of ranged weapons, along with an API to easily make your own ones - ClockGen/bweapons_modpack" />
    <meta property="og:image" content="https://avatars0.githubusercontent.com/u/12116711?s=400&v=4" /><meta property="og:site_name" content="GitHub" /><meta property="og:type" content="object" /><meta property="og:title" content="ClockGen/bweapons_modpack" /><meta property="og:url" content="https://github.com/ClockGen/bweapons_modpack" /><meta property="og:description" content="A weapons modpack aiming at providing a complete, technologic and magic progression of ranged weapons, along with an API to easily make your own ones - ClockGen/bweapons_modpack" />

  <link rel="assets" href="https://github.githubassets.com/">

    <meta name="request-id" content="B8FE:5FB3:29CA2C7:45ADE34:5DE5F78B" data-pjax-transient>

  <meta name="selected-link" value="repo_source" data-pjax-transient>

      <meta name="google-site-verification" content="KT5gs8h0wvaagLKAVWq8bbeNwnZZK1r1XQysX3xurLU">
    <meta name="google-site-verification" content="ZzhVyEFwb7w3e0-uOTltm8Jsck2F5StVihD0exw2fsA">
    <meta name="google-site-verification" content="GXs5KoUUkNCoaAZn7wPN-t01Pywp9M3sEjnt_3_ZWPc">

    <meta name="octolytics-host" content="collector.githubapp.com" /><meta name="octolytics-app-id" content="github" /><meta name="octolytics-event-url" content="https://collector.githubapp.com/github-external/browser_event" /><meta name="octolytics-dimension-request_id" content="B8FE:5FB3:29CA2C7:45ADE34:5DE5F78B" /><meta name="octolytics-dimension-region_edge" content="iad" /><meta name="octolytics-dimension-region_render" content="iad" /><meta name="octolytics-dimension-ga_id" content="" class="js-octo-ga-id" /><meta name="octolytics-dimension-visitor_id" content="9034041539195958962" />

<meta name="analytics-location" content="/<user-name>/<repo-name>/blob/show" data-pjax-transient="true" />

    <meta name="google-analytics" content="UA-3769691-2">

<meta class="js-ga-set" name="dimension1" content="Logged Out">

      <meta name="hostname" content="github.com">
    <meta name="user-login" content="">

      <meta name="expected-hostname" content="github.com">

      <meta name="js-proxy-site-detection-payload" content="YTc0ODg5OTZmOGFjMmJhZjIxOWFkOTg0ZDQwOGQzOTVhYTI1ODBlMmU4YmEzZjJiZjdmOWI4NTVlNTdmNjEyZnx7InJlbW90ZV9hZGRyZXNzIjoiMTcyLjE1LjIwNy4xMjkiLCJyZXF1ZXN0X2lkIjoiQjhGRTo1RkIzOjI5Q0EyQzc6NDVBREUzNDo1REU1Rjc4QiIsInRpbWVzdGFtcCI6MTU3NTM1MjIwOCwiaG9zdCI6ImdpdGh1Yi5jb20ifQ==">

    <meta name="enabled-features" content="MARKETPLACE_FEATURED_BLOG_POSTS,MARKETPLACE_INVOICED_BILLING,MARKETPLACE_SOCIAL_PROOF_CUSTOMERS,MARKETPLACE_TRENDING_SOCIAL_PROOF,MARKETPLACE_RECOMMENDATIONS,MARKETPLACE_PENDING_INSTALLATIONS">

    <meta name="html-safe-nonce" content="9069d4aad5e030971c848bb0f5d392b82c6ad7de">

  <meta http-equiv="x-pjax-version" content="8e662d4eb6cf48a7cb17fc69344f4b03">

      <link href="https://github.com/ClockGen/bweapons_modpack/commits/master.atom" rel="alternate" title="Recent Commits to bweapons_modpack:master" type="application/atom+xml">

  <meta name="go-import" content="github.com/ClockGen/bweapons_modpack git https://github.com/ClockGen/bweapons_modpack.git">

  <meta name="octolytics-dimension-user_id" content="12116711" /><meta name="octolytics-dimension-user_login" content="ClockGen" /><meta name="octolytics-dimension-repository_id" content="200735007" /><meta name="octolytics-dimension-repository_nwo" content="ClockGen/bweapons_modpack" /><meta name="octolytics-dimension-repository_public" content="true" /><meta name="octolytics-dimension-repository_is_fork" content="false" /><meta name="octolytics-dimension-repository_network_root_id" content="200735007" /><meta name="octolytics-dimension-repository_network_root_nwo" content="ClockGen/bweapons_modpack" /><meta name="octolytics-dimension-repository_explore_github_marketplace_ci_cta_shown" content="false" />

    <link rel="canonical" href="https://github.com/ClockGen/bweapons_modpack/blob/master/bweapons_magic_pack/sounds/bweapons_magic_pack_reload.ogg" data-pjax-transient>

  <meta name="browser-stats-url" content="https://api.github.com/_private/browser/stats">

  <meta name="browser-errors-url" content="https://api.github.com/_private/browser/errors">

  <link rel="mask-icon" href="https://github.githubassets.com/pinned-octocat.svg" color="#000000">
  <link rel="icon" type="image/x-icon" class="js-site-favicon" href="https://github.githubassets.com/favicon.ico">

<meta name="theme-color" content="#1e2327">

  <link rel="manifest" href="/manifest.json" crossOrigin="use-credentials">

    Skip to content

          <svg height="32" class="octicon octicon-mark-github text-white" viewBox="0 0 16 16" version="1.1" width="32" aria-hidden="true"><path fill-rule="evenodd" d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"/></svg>

              <svg class="octicon octicon-repo" viewBox="0 0 12 16" version="1.1" width="12" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M4 9H3V8h1v1zm0-3H3v1h1V6zm0-2H3v1h1V4zm0-2H3v1h1V2zm8-1v12c0 .55-.45 1-1 1H6v2l-1.5-1.5L3 16v-2H1c-.55 0-1-.45-1-1V1c0-.55.45-1 1-1h10c.55 0 1 .45 1 1zm-1 10H1v2h2v-1h3v1h5v-2zm0-10H2v9h9V1z"/></svg>
    ClockGen
    /
    bweapons_modpack

            <a href="/join?source=header-repo"
              class="d-inline-block d-lg-none f5 text-white no-underline border border-gray-dark rounded-2 px-2 py-1 mr-3 mr-sm-5"
              data-hydro-click="{"event_type":"authentication.click","payload":{"location_in_page":"site header","repository_id":null,"auth_type":"SIGN_UP","client_id":"2103401706.1575351986","originating_request_id":"B8FE:5FB3:29CA2C7:45ADE34:5DE5F78B","originating_url":"https://github.com/ClockGen/bweapons_modpack/blob/master/bweapons_magic_pack/sounds/bweapons_magic_pack_reload.ogg","referrer":"https://github.com/ClockGen/bweapons_modpack/tree/master/bweapons_magic_pack/sounds","user_id":null}}" data-hydro-click-hmac="935a32ddb91494a812b769626323f2b5a5714e78849731920411a5525b168f98"
              data-ga-click="(Logged out) Header, clicked Sign up, text:sign-up">
              Sign up

          <button class="btn-link d-lg-none mt-1 js-details-target" type="button" aria-label="Toggle navigation" aria-expanded="false">
            <svg height="24" class="octicon octicon-three-bars text-white" viewBox="0 0 12 16" version="1.1" width="18" aria-hidden="true"><path fill-rule="evenodd" d="M11.41 9H.59C0 9 0 8.59 0 8c0-.59 0-1 .59-1H11.4c.59 0 .59.41.59 1 0 .59 0 1-.59 1h.01zm0-4H.59C0 5 0 4.59 0 4c0-.59 0-1 .59-1H11.4c.59 0 .59.41.59 1 0 .59 0 1-.59 1h.01zM.59 11H11.4c.59 0 .59.41.59 1 0 .59 0 1-.59 1H.59C0 13 0 12.59 0 12c0-.59 0-1 .59-1z"/></svg>
          </button>

        <button class="btn-link js-details-target" type="button" aria-label="Toggle navigation" aria-expanded="false">
          <svg height="24" class="octicon octicon-x text-gray" viewBox="0 0 12 16" version="1.1" width="18" aria-hidden="true"><path fill-rule="evenodd" d="M7.48 8l3.75 3.75-1.48 1.48L6 9.48l-3.75 3.75-1.48-1.48L4.52 8 .77 4.25l1.48-1.48L6 6.52l3.75-3.75 1.48 1.48L7.48 8z"/></svg>
        </button>

                    Why GitHub?
                    <svg x="0px" y="0px" viewBox="0 0 14 8" xml:space="preserve" fill="none" class="icon-chevon-down-mktg position-absolute position-lg-relative">
                      <path d="M1,1l6.2,6L13,1"></path>
                    </svg>

                    Features →

Code review

Project management

Integrations

Actions

Packages

Security

Team management

Social coding

Documentation

Code hosting

Customer stories →

Security →

                Enterprise

                    Explore
                    <svg x="0px" y="0px" viewBox="0 0 14 8" xml:space="preserve" fill="none" class="icon-chevon-down-mktg position-absolute position-lg-relative">
                      <path d="M1,1l6.2,6L13,1"></path>
                    </svg>

Explore GitHub →

Learn & contribute

Topics

Collections

Trending

Learning Lab

Open source guides

Connect with others

Events

Community forum

GitHub Education

                Marketplace

                    Pricing
                    <svg x="0px" y="0px" viewBox="0 0 14 8" xml:space="preserve" fill="none" class="icon-chevon-down-mktg position-absolute position-lg-relative">
                       <path d="M1,1l6.2,6L13,1"></path>
                    </svg>

                    Plans →

Compare plans

Contact Sales

Nonprofit →

Education →

    </option></form><form class="js-site-search-form" role="search" aria-label="Site" data-scope-type="Repository" data-scope-id="200735007" data-scoped-search-url="/ClockGen/bweapons_modpack/search" data-unscoped-search-url="/search" action="/ClockGen/bweapons_modpack/search" accept-charset="UTF-8" method="get"><input name="utf8" type="hidden" value="✓" />

        <input type="text"
          class="form-control input-sm header-search-input jump-to-field js-jump-to-field js-site-search-focus js-site-search-field is-clearable"
          data-hotkey="s,/"
          name="q"
          value=""
          placeholder="Search"
          data-unscoped-placeholder="Search GitHub"
          data-scoped-placeholder="Search"
          autocapitalize="off"
          aria-autocomplete="list"
          aria-controls="jump-to-results"
          aria-label="Search"
          data-jump-to-suggestions-path="/_graphql/GetSuggestedNavigationDestinations#csrf-token=B3MUNzFZsn9FT6SWBQc0JeuGNC8wzwmhCEURAzNrEySXw6zWlYcO353M76oLjehPvq/YtgSo/VlBOvbVlVXScA=="
          spellcheck="false"
          autocomplete="off"
          >
          <input type="hidden" class="js-site-search-type-field" name="type" >

      <svg height="16" width="16" class="octicon octicon-repo flex-shrink-0 js-jump-to-octicon-repo d-none" title="Repository" aria-label="Repository" viewBox="0 0 12 16" version="1.1" role="img"><path fill-rule="evenodd" d="M4 9H3V8h1v1zm0-3H3v1h1V6zm0-2H3v1h1V4zm0-2H3v1h1V2zm8-1v12c0 .55-.45 1-1 1H6v2l-1.5-1.5L3 16v-2H1c-.55 0-1-.45-1-1V1c0-.55.45-1 1-1h10c.55 0 1 .45 1 1zm-1 10H1v2h2v-1h3v1h5v-2zm0-10H2v9h9V1z"/></svg>
      <svg height="16" width="16" class="octicon octicon-project flex-shrink-0 js-jump-to-octicon-project d-none" title="Project" aria-label="Project" viewBox="0 0 15 16" version="1.1" role="img"><path fill-rule="evenodd" d="M10 12h3V2h-3v10zm-4-2h3V2H6v8zm-4 4h3V2H2v12zm-1 1h13V1H1v14zM14 0H1a1 1 0 00-1 1v14a1 1 0 001 1h13a1 1 0 001-1V1a1 1 0 00-1-1z"/></svg>
      <svg height="16" width="16" class="octicon octicon-search flex-shrink-0 js-jump-to-octicon-search d-none" title="Search" aria-label="Search" viewBox="0 0 16 16" version="1.1" role="img"><path fill-rule="evenodd" d="M15.7 13.3l-3.81-3.83A5.93 5.93 0 0013 6c0-3.31-2.69-6-6-6S1 2.69 1 6s2.69 6 6 6c1.3 0 2.48-.41 3.47-1.11l3.83 3.81c.19.2.45.3.7.3.25 0 .52-.09.7-.3a.996.996 0 000-1.41v.01zM7 10.7c-2.59 0-4.7-2.11-4.7-4.7 0-2.59 2.11-4.7 4.7-4.7 2.59 0 4.7 2.11 4.7 4.7 0 2.59-2.11 4.7-4.7 4.7z"/></svg>

        In this repository

        All GitHub

      ↵

      Jump to
      ↵

    No suggested jump to results

      <svg height="16" width="16" class="octicon octicon-repo flex-shrink-0 js-jump-to-octicon-repo d-none" title="Repository" aria-label="Repository" viewBox="0 0 12 16" version="1.1" role="img"><path fill-rule="evenodd" d="M4 9H3V8h1v1zm0-3H3v1h1V6zm0-2H3v1h1V4zm0-2H3v1h1V2zm8-1v12c0 .55-.45 1-1 1H6v2l-1.5-1.5L3 16v-2H1c-.55 0-1-.45-1-1V1c0-.55.45-1 1-1h10c.55 0 1 .45 1 1zm-1 10H1v2h2v-1h3v1h5v-2zm0-10H2v9h9V1z"/></svg>
      <svg height="16" width="16" class="octicon octicon-project flex-shrink-0 js-jump-to-octicon-project d-none" title="Project" aria-label="Project" viewBox="0 0 15 16" version="1.1" role="img"><path fill-rule="evenodd" d="M10 12h3V2h-3v10zm-4-2h3V2H6v8zm-4 4h3V2H2v12zm-1 1h13V1H1v14zM14 0H1a1 1 0 00-1 1v14a1 1 0 001 1h13a1 1 0 001-1V1a1 1 0 00-1-1z"/></svg>
      <svg height="16" width="16" class="octicon octicon-search flex-shrink-0 js-jump-to-octicon-search d-none" title="Search" aria-label="Search" viewBox="0 0 16 16" version="1.1" role="img"><path fill-rule="evenodd" d="M15.7 13.3l-3.81-3.83A5.93 5.93 0 0013 6c0-3.31-2.69-6-6-6S1 2.69 1 6s2.69 6 6 6c1.3 0 2.48-.41 3.47-1.11l3.83 3.81c.19.2.45.3.7.3.25 0 .52-.09.7-.3a.996.996 0 000-1.41v.01zM7 10.7c-2.59 0-4.7-2.11-4.7-4.7 0-2.59 2.11-4.7 4.7-4.7 2.59 0 4.7 2.11 4.7 4.7 0 2.59-2.11 4.7-4.7 4.7z"/></svg>

        In this repository

        All GitHub

      ↵

      Jump to
      ↵

      <svg height="16" width="16" class="octicon octicon-repo flex-shrink-0 js-jump-to-octicon-repo d-none" title="Repository" aria-label="Repository" viewBox="0 0 12 16" version="1.1" role="img"><path fill-rule="evenodd" d="M4 9H3V8h1v1zm0-3H3v1h1V6zm0-2H3v1h1V4zm0-2H3v1h1V2zm8-1v12c0 .55-.45 1-1 1H6v2l-1.5-1.5L3 16v-2H1c-.55 0-1-.45-1-1V1c0-.55.45-1 1-1h10c.55 0 1 .45 1 1zm-1 10H1v2h2v-1h3v1h5v-2zm0-10H2v9h9V1z"/></svg>
      <svg height="16" width="16" class="octicon octicon-project flex-shrink-0 js-jump-to-octicon-project d-none" title="Project" aria-label="Project" viewBox="0 0 15 16" version="1.1" role="img"><path fill-rule="evenodd" d="M10 12h3V2h-3v10zm-4-2h3V2H6v8zm-4 4h3V2H2v12zm-1 1h13V1H1v14zM14 0H1a1 1 0 00-1 1v14a1 1 0 001 1h13a1 1 0 001-1V1a1 1 0 00-1-1z"/></svg>
      <svg height="16" width="16" class="octicon octicon-search flex-shrink-0 js-jump-to-octicon-search d-none" title="Search" aria-label="Search" viewBox="0 0 16 16" version="1.1" role="img"><path fill-rule="evenodd" d="M15.7 13.3l-3.81-3.83A5.93 5.93 0 0013 6c0-3.31-2.69-6-6-6S1 2.69 1 6s2.69 6 6 6c1.3 0 2.48-.41 3.47-1.11l3.83 3.81c.19.2.45.3.7.3.25 0 .52-.09.7-.3a.996.996 0 000-1.41v.01zM7 10.7c-2.59 0-4.7-2.11-4.7-4.7 0-2.59 2.11-4.7 4.7-4.7 2.59 0 4.7 2.11 4.7 4.7 0 2.59-2.11 4.7-4.7 4.7z"/></svg>

        In this repository

        All GitHub

      ↵

      Jump to
      ↵

</form>

        <a href="/login?return_to=%2FClockGen%2Fbweapons_modpack%2Fblob%2Fmaster%2Fbweapons_magic_pack%2Fsounds%2Fbweapons_magic_pack_reload.ogg"
          class="HeaderMenu-link no-underline mr-3"
          data-hydro-click="{"event_type":"authentication.click","payload":{"location_in_page":"site header menu","repository_id":null,"auth_type":"SIGN_UP","client_id":"2103401706.1575351986","originating_request_id":"B8FE:5FB3:29CA2C7:45ADE34:5DE5F78B","originating_url":"https://github.com/ClockGen/bweapons_modpack/blob/master/bweapons_magic_pack/sounds/bweapons_magic_pack_reload.ogg","referrer":"https://github.com/ClockGen/bweapons_modpack/tree/master/bweapons_magic_pack/sounds","user_id":null}}" data-hydro-click-hmac="286cc6825539bd35d2aa83d30c43f040c7ff035409079dd5d25f2195581cc4ee"
          data-ga-click="(Logged out) Header, clicked Sign in, text:sign-in">
          Sign in

          <a href="/join?source=header-repo&source_repo=ClockGen%2Fbweapons_modpack"
            class="HeaderMenu-link d-inline-block no-underline border border-gray-dark rounded-1 px-2 py-1"
            data-hydro-click="{"event_type":"authentication.click","payload":{"location_in_page":"site header menu","repository_id":null,"auth_type":"SIGN_UP","client_id":"2103401706.1575351986","originating_request_id":"B8FE:5FB3:29CA2C7:45ADE34:5DE5F78B","originating_url":"https://github.com/ClockGen/bweapons_modpack/blob/master/bweapons_magic_pack/sounds/bweapons_magic_pack_reload.ogg","referrer":"https://github.com/ClockGen/bweapons_modpack/tree/master/bweapons_magic_pack/sounds","user_id":null}}" data-hydro-click-hmac="286cc6825539bd35d2aa83d30c43f040c7ff035409079dd5d25f2195581cc4ee"
            data-ga-click="(Logged out) Header, clicked Sign up, text:sign-up">
            Sign up

  <a class="tooltipped tooltipped-s btn btn-sm btn-with-count" aria-label="You must be signed in to watch a repository" rel="nofollow" data-hydro-click="{"event_type":"authentication.click","payload":{"location_in_page":"notification subscription menu watch","repository_id":null,"auth_type":"LOG_IN","client_id":"2103401706.1575351986","originating_request_id":"B8FE:5FB3:29CA2C7:45ADE34:5DE5F78B","originating_url":"https://github.com/ClockGen/bweapons_modpack/blob/master/bweapons_magic_pack/sounds/bweapons_magic_pack_reload.ogg","referrer":"https://github.com/ClockGen/bweapons_modpack/tree/master/bweapons_magic_pack/sounds","user_id":null}}" data-hydro-click-hmac="c75bacea187be5abb031bb480f1a7ab0022f1de360b9f0b2ca9c1f2a6427c3bf" href="/login?return_to=%2FClockGen%2Fbweapons_modpack">
    <svg class="octicon octicon-eye v-align-text-bottom" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M8.06 2C3 2 0 8 0 8s3 6 8.06 6C13 14 16 8 16 8s-3-6-7.94-6zM8 12c-2.2 0-4-1.78-4-4 0-2.2 1.8-4 4-4 2.22 0 4 1.8 4 4 0 2.22-1.78 4-4 4zm2-4c0 1.11-.89 2-2 2-1.11 0-2-.89-2-2 0-1.11.89-2 2-2 1.11 0 2 .89 2 2z"/></svg>
    Watch

      1

        <a class="btn btn-sm btn-with-count tooltipped tooltipped-s" aria-label="You must be signed in to star a repository" rel="nofollow" data-hydro-click="{"event_type":"authentication.click","payload":{"location_in_page":"star button","repository_id":200735007,"auth_type":"LOG_IN","client_id":"2103401706.1575351986","originating_request_id":"B8FE:5FB3:29CA2C7:45ADE34:5DE5F78B","originating_url":"https://github.com/ClockGen/bweapons_modpack/blob/master/bweapons_magic_pack/sounds/bweapons_magic_pack_reload.ogg","referrer":"https://github.com/ClockGen/bweapons_modpack/tree/master/bweapons_magic_pack/sounds","user_id":null}}" data-hydro-click-hmac="5204bcc96cec35ef8a036d61c55e9d69f6b79a258f5c0d3a4ec861bc0a77a260" href="/login?return_to=%2FClockGen%2Fbweapons_modpack">
      <svg class="octicon octicon-star v-align-text-bottom" viewBox="0 0 14 16" version="1.1" width="14" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M14 6l-4.9-.64L7 1 4.9 5.36 0 6l3.6 3.26L2.67 14 7 11.67 11.33 14l-.93-4.74L14 6z"/></svg>
      Star

      0

      <a class="btn btn-sm btn-with-count tooltipped tooltipped-s" aria-label="You must be signed in to fork a repository" rel="nofollow" data-hydro-click="{"event_type":"authentication.click","payload":{"location_in_page":"repo details fork button","repository_id":200735007,"auth_type":"LOG_IN","client_id":"2103401706.1575351986","originating_request_id":"B8FE:5FB3:29CA2C7:45ADE34:5DE5F78B","originating_url":"https://github.com/ClockGen/bweapons_modpack/blob/master/bweapons_magic_pack/sounds/bweapons_magic_pack_reload.ogg","referrer":"https://github.com/ClockGen/bweapons_modpack/tree/master/bweapons_magic_pack/sounds","user_id":null}}" data-hydro-click-hmac="99593c7abb451d76e0568d20fa8cf93fbbf130547fd62a9057d054da34f661b1" href="/login?return_to=%2FClockGen%2Fbweapons_modpack">
        <svg class="octicon octicon-repo-forked v-align-text-bottom" viewBox="0 0 10 16" version="1.1" width="10" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M8 1a1.993 1.993 0 00-1 3.72V6L5 8 3 6V4.72A1.993 1.993 0 002 1a1.993 1.993 0 00-1 3.72V6.5l3 3v1.78A1.993 1.993 0 005 15a1.993 1.993 0 001-3.72V9.5l3-3V4.72A1.993 1.993 0 008 1zM2 4.2C1.34 4.2.8 3.65.8 3c0-.65.55-1.2 1.2-1.2.65 0 1.2.55 1.2 1.2 0 .65-.55 1.2-1.2 1.2zm3 10c-.66 0-1.2-.55-1.2-1.2 0-.65.55-1.2 1.2-1.2.65 0 1.2.55 1.2 1.2 0 .65-.55 1.2-1.2 1.2zm3-10c-.66 0-1.2-.55-1.2-1.2 0-.65.55-1.2 1.2-1.2.65 0 1.2.55 1.2 1.2 0 .65-.55 1.2-1.2 1.2z"/></svg>
        Fork

      0

    <svg class="octicon octicon-repo" viewBox="0 0 12 16" version="1.1" width="12" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M4 9H3V8h1v1zm0-3H3v1h1V6zm0-2H3v1h1V4zm0-2H3v1h1V2zm8-1v12c0 .55-.45 1-1 1H6v2l-1.5-1.5L3 16v-2H1c-.55 0-1-.45-1-1V1c0-.55.45-1 1-1h10c.55 0 1 .45 1 1zm-1 10H1v2h2v-1h3v1h5v-2zm0-10H2v9h9V1z"/></svg>
  ClockGen/bweapons_modpack

      <svg class="octicon octicon-code" viewBox="0 0 14 16" version="1.1" width="14" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M9.5 3L8 4.5 11.5 8 8 11.5 9.5 13 14 8 9.5 3zm-5 0L0 8l4.5 5L6 11.5 2.5 8 6 4.5 4.5 3z"/></svg>
      Code
      <meta itemprop="position" content="1">

        <svg class="octicon octicon-issue-opened" viewBox="0 0 14 16" version="1.1" width="14" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 011.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"/></svg>
        Issues
        0
        <meta itemprop="position" content="2">

      <svg class="octicon octicon-git-pull-request" viewBox="0 0 12 16" version="1.1" width="12" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M11 11.28V5c-.03-.78-.34-1.47-.94-2.06C9.46 2.35 8.78 2.03 8 2H7V0L4 3l3 3V4h1c.27.02.48.11.69.31.21.2.3.42.31.69v6.28A1.993 1.993 0 0010 15a1.993 1.993 0 001-3.72zm-1 2.92c-.66 0-1.2-.55-1.2-1.2 0-.65.55-1.2 1.2-1.2.65 0 1.2.55 1.2 1.2 0 .65-.55 1.2-1.2 1.2zM4 3c0-1.11-.89-2-2-2a1.993 1.993 0 00-1 3.72v6.56A1.993 1.993 0 002 15a1.993 1.993 0 001-3.72V4.72c.59-.34 1-.98 1-1.72zm-.8 10c0 .66-.55 1.2-1.2 1.2-.65 0-1.2-.55-1.2-1.2 0-.65.55-1.2 1.2-1.2.65 0 1.2.55 1.2 1.2zM2 4.2C1.34 4.2.8 3.65.8 3c0-.65.55-1.2 1.2-1.2.65 0 1.2.55 1.2 1.2 0 .65-.55 1.2-1.2 1.2z"/></svg>
      Pull requests
      0
      <meta itemprop="position" content="3">

      <svg class="octicon octicon-project" viewBox="0 0 15 16" version="1.1" width="15" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M10 12h3V2h-3v10zm-4-2h3V2H6v8zm-4 4h3V2H2v12zm-1 1h13V1H1v14zM14 0H1a1 1 0 00-1 1v14a1 1 0 001 1h13a1 1 0 001-1V1a1 1 0 00-1-1z"/></svg>
      Projects
      0

      <svg class="octicon octicon-shield" viewBox="0 0 14 16" version="1.1" width="14" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M0 2l7-2 7 2v6.02C14 12.69 8.69 16 7 16c-1.69 0-7-3.31-7-7.98V2zm1 .75L7 1l6 1.75v5.268C13 12.104 8.449 15 7 15c-1.449 0-6-2.896-6-6.982V2.75zm1 .75L7 2v12c-1.207 0-5-2.482-5-5.985V3.5z"/></svg>
      Security

      <svg class="octicon octicon-graph" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M16 14v1H0V0h1v14h15zM5 13H3V8h2v5zm4 0H7V3h2v10zm4 0h-2V6h2v7z"/></svg>
      Insights

        Code
        <meta itemprop="position" content="1">

          Issues
          0
          <meta itemprop="position" content="2">

        Pull requests
        0
        <meta itemprop="position" content="3">

          Projects
          0
          <meta itemprop="position" content="4">

        Security
        <meta itemprop="position" content="6">

        Pulse

    Permalink

          </option></form><form action="/prompt_dismissals/signup" accept-charset="UTF-8" method="post"><input name="utf8" type="hidden" value="✓" /><input type="hidden" name="_method" value="put" /><input type="hidden" name="authenticity_token" value="yAfOPzJE8vX/HBu7rPSxZ8hfk/ejm+u6vht9xqpBYTKcd5kifFIHRpDh2c9R5/cL2NoogxbljnEU36SQe8r21A==" />
            <button type="submit" class="position-absolute top-0 right-0 btn-link link-gray" data-ga-click="(Logged out) Sign up prompt, clicked Dismiss, text:dismiss">
              Dismiss
            </button>
</form>
Join GitHub today

GitHub is home to over 40 million developers working together to host and review code, manage projects, and build software together.

          <a class="btn btn-primary" data-hydro-click="{"event_type":"authentication.click","payload":{"location_in_page":"files signup prompt","repository_id":null,"auth_type":"SIGN_UP","client_id":"2103401706.1575351986","originating_request_id":"B8FE:5FB3:29CA2C7:45ADE34:5DE5F78B","originating_url":"https://github.com/ClockGen/bweapons_modpack/blob/master/bweapons_magic_pack/sounds/bweapons_magic_pack_reload.ogg","referrer":"https://github.com/ClockGen/bweapons_modpack/tree/master/bweapons_magic_pack/sounds","user_id":null}}" data-hydro-click-hmac="38033469d63f561d1c727ace01ddb5b3b78d3c2e35b05a9b9610e3ccbf7e80e8" data-ga-click="(Logged out) Sign up prompt, clicked Sign up, text:sign-up" href="/join?source=prompt-blob-show&source_repo=ClockGen%2Fbweapons_modpack">Sign up

    Branch:
    master

    <include-fragment class="select-menu-loading-overlay anim-pulse">
      <svg height="32" class="octicon octicon-octoface" viewBox="0 0 16 16" version="1.1" width="32" aria-hidden="true"><path fill-rule="evenodd" d="M14.7 5.34c.13-.32.55-1.59-.13-3.31 0 0-1.05-.33-3.44 1.3-1-.28-2.07-.32-3.13-.32s-2.13.04-3.13.32c-2.39-1.64-3.44-1.3-3.44-1.3-.68 1.72-.26 2.99-.13 3.31C.49 6.21 0 7.33 0 8.69 0 13.84 3.33 15 7.98 15S16 13.84 16 8.69c0-1.36-.49-2.48-1.3-3.35zM8 14.02c-3.3 0-5.98-.15-5.98-3.35 0-.76.38-1.48 1.02-2.07 1.07-.98 2.9-.46 4.96-.46 2.07 0 3.88-.52 4.96.46.65.59 1.02 1.3 1.02 2.07 0 3.19-2.68 3.35-5.98 3.35zM5.49 9.01c-.66 0-1.2.8-1.2 1.78s.54 1.79 1.2 1.79c.66 0 1.2-.8 1.2-1.79s-.54-1.78-1.2-1.78zm5.02 0c-.66 0-1.2.79-1.2 1.78s.54 1.79 1.2 1.79c.66 0 1.2-.8 1.2-1.79s-.53-1.78-1.2-1.78z"/></svg>
    </include-fragment>

            Find file

          <clipboard-copy value="bweapons_magic_pack/sounds/bweapons_magic_pack_reload.ogg" class="btn btn-sm BtnGroup-item">
            Copy path
          </clipboard-copy>

        bweapons_modpack/bweapons_magic_pack/sounds/bweapons_magic_pack_reload.ogg

          Find file

        <clipboard-copy value="bweapons_magic_pack/sounds/bweapons_magic_pack_reload.ogg" class="btn btn-sm BtnGroup-item">
          Copy path
        </clipboard-copy>

    <include-fragment src="/ClockGen/bweapons_modpack/contributors/master/bweapons_magic_pack/sounds/bweapons_magic_pack_reload.ogg" class="Box Box--condensed commit-loader">

        Fetching contributors…

        Cannot retrieve contributors at this time

</include-fragment>

    20.6 KB

      Download
      History

          </option></form><form class="inline-form" action="/ClockGen/bweapons_modpack/delete/master/bweapons_magic_pack/sounds/bweapons_magic_pack_reload.ogg" accept-charset="UTF-8" method="post"><input name="utf8" type="hidden" value="✓" /><input type="hidden" name="authenticity_token" value="H0pP5gezSEBzyMftQqTpy9WrPMzg1+pccAp63GOsYGKOZBAjSPnJzXUI64MXvfzWr02lsJd9HmgqABovubxXBg==" />
            <button class="btn-octicon btn-octicon-danger tooltipped tooltipped-nw" type="submit"
              aria-label="You must be signed in to make or propose changes" data-disable-with>
              <svg class="octicon octicon-trashcan" viewBox="0 0 12 16" version="1.1" width="12" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M11 2H9c0-.55-.45-1-1-1H5c-.55 0-1 .45-1 1H2c-.55 0-1 .45-1 1v1c0 .55.45 1 1 1v9c0 .55.45 1 1 1h7c.55 0 1-.45 1-1V5c.55 0 1-.45 1-1V3c0-.55-.45-1-1-1zm-1 12H3V5h1v8h1V5h1v8h1V5h1v8h1V5h1v9zm1-10H2V3h9v1z"/></svg>
            </button>
</form>

          View raw

      </option></form><form class="js-jump-to-line-form Box-body d-flex" action="" accept-charset="UTF-8" method="get"><input name="utf8" type="hidden" value="✓" />
        <input class="form-control flex-auto mr-3 linejump-input js-jump-to-line-field" type="text" placeholder="Jump to line…" aria-label="Jump to line" autofocus>
        <button type="submit" class="btn" data-close-dialog>Go</button>
</form>

© 2019 GitHub, Inc.

Terms

Privacy

Security

Status

Help

      <svg height="24" class="octicon octicon-mark-github" viewBox="0 0 16 16" version="1.1" width="24" aria-hidden="true"><path fill-rule="evenodd" d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"/></svg>

Contact GitHub

Pricing

API

Training

Blog

About

    <svg class="octicon octicon-alert" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 000 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 00.01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"/></svg>
    <button type="button" class="flash-close js-ajax-error-dismiss" aria-label="Dismiss error">
      <svg class="octicon octicon-x" viewBox="0 0 12 16" version="1.1" width="12" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M7.48 8l3.75 3.75-1.48 1.48L6 9.48l-3.75 3.75-1.48-1.48L4.52 8 .77 4.25l1.48-1.48L6 6.52l3.75-3.75 1.48 1.48L7.48 8z"/></svg>
    </button>
    You can’t perform that action at this time.

    <svg class="octicon octicon-alert" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 000 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 00.01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"/></svg>
    You signed in with another tab or window. Reload to refresh your session.
    You signed out in another tab or window. Reload to refresh your session.

  <template id="site-details-dialog">

      <button class="Box-btn-octicon m-0 btn-octicon position-absolute right-0 top-0" type="button" aria-label="Close dialog" data-close-dialog>
        <svg class="octicon octicon-x" viewBox="0 0 12 16" version="1.1" width="12" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M7.48 8l3.75 3.75-1.48 1.48L6 9.48l-3.75 3.75-1.48-1.48L4.52 8 .77 4.25l1.48-1.48L6 6.52l3.75-3.75 1.48 1.48L7.48 8z"/></svg>
      </button>

</template>


code:
MeseCraft mod: mese_companion_cube
==========================

License of source code
----------------------
by Napiophelios
WTFPL

License of textures
-------------------
nbea_mese_companion.png, Napiophelios, CC-BY-3.0

https://creativecommons.org/licenses/by/3.0/

repo_name: korkut/MeseCraft

license: mit


code:
name = mese_companion_cube
author = MeseCraft
description = Adds a Mese Companion Cube that looks cool and is a mesecon activator.


code:
on: [push, pull_request]
name: Check & Release

jobs:
  lint:
    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@main
    - name: apt
      run: sudo apt-get install -y luarocks
    - name: luacheck install
      run: luarocks install --local luacheck
    - name: luacheck run
      run: $HOME/.luarocks/bin/luacheck ./


code:
std = "lua51c"

ignore = {
	"21/_+", -- Unused variable, except "_", "__", etc.
	"213", -- Unused loop variable
	"421", -- Shadowing a local variable
	"422", -- Shadowing an argument
	"423", -- Shadowing a loop variable
	"431", -- Shadowing an upvalue
	"432", -- Shadowing an upvalue argument
	"433", -- Shadowing an upvalue loop variable
	"542", -- Empty if branch
}

max_line_length = 200

read_globals = {
	"default",
	"digiline",
	"doors",
	"dump",
	"jit",
	"minetest",
	"screwdriver",
	"string.split",
	"table.copy",
	"table.insert_all",
	"vector",
	"VoxelArea",
}

globals = {"mesecon"}

files["mesecons/actionqueue.lua"] = {
	globals = {"minetest.registered_globalsteps"},
}


code:
    ########################################################################
    ##  __    __   _____   _____   _____   _____   _____   _   _   _____  ##
    ## |  \  /  | |  ___| |  ___| |  ___| |  ___| |  _  | | \ | | |  ___| ##
    ## |   \/   | | |___  | |___  | |___  | |     | | | | |  \| | | |___  ##
    ## | |\__/| | |  ___| |___  | |  ___| | |     | | | | |     | |___  | ##
    ## | |    | | | |___   ___| | | |___  | |___  | |_| | | |\  |  ___| | ##
    ## |_|    |_| |_____| |_____| |_____| |_____| |_____| |_| \_| |_____| ##
    ##                                                                    ##
    ########################################################################

MESECONS by Jeija and contributors

Mezzee-what?
------------
[Mesecons](https://mesecons.net/)! They're yellow, they're conductive, and they'll add a whole new dimension to Minetest's gameplay.

Mesecons is a mod for [Minetest](https://www.minetest.net/) that implements a ton of items related to digital circuitry, such as wires, buttons, lights, and even programmable controllers. Among other things, there are also pistons, solar panels, pressure plates, and note blocks.

Mesecons has a similar goal to Redstone in Minecraft, but works in its own way, with different rules and mechanics.

OK, I want in.
--------------
Go get it!

[DOWNLOAD IT NOW](https://github.com/minetest-mods/mesecons/archive/master.zip)

Now go ahead and install it like any other Minetest mod. Don't know how? Check out [the wonderful page about it](https://wiki.minetest.net/Mods) over at the official Minetest Wiki. For your convenience, here's a quick summary:

1. If Mesecons is still in a ZIP file, extract the folder inside to somewhere on the computer.
2. Make sure that when you open the folder, you can directly find `README.md` in the listing. If you just see another folder, move that folder up one level and delete the old one.
3. Open up the Minetest mods folder - usually `/mods/`. If you see the `minetest` or folder inside of that, that is your mod folder instead.
4. Copy the Mesecons folder into the mods folder.

Don't like some parts of Mesecons? Open up the Mesecons folder and delete the subfolder containing the mod you don't want. If you didn't want movestones, for example, all you have to do is delete the `mesecons_movestones` folder and they will no longer be available.

There are no dependencies - it will work right after installing!

How do I use this thing?
------------------------
How about a [quick overview video](https://www.youtube.com/watch?v=6kmeQj6iW5k)?

Or maybe a [comprehensive reference](http://mesecons.net/items.html) is your style?

An overview for the very newest of new beginners? How does [this one](http://uberi.mesecons.net/projects/MeseconsBasics/index.html) look?

There is also a [wiki page](https://wiki.minetest.net/Mods/Mesecons) dedicated to this mod.

Want to get more into building? Why not check out the [Mesecons Laboratory](http://uberi.mesecons.net/), a website dedicated to advanced Mesecons builders?

Want to contribute to Mesecons itself? Check out the [source code](https://github.com/minetest-mods/mesecons)!

Who wrote it anyways?
---------------------
These awesome people made Mesecons possible!

| Contributor     | Contribution                     |
| --------------- | -------------------------------- |
| Hawk777         | Code for VoxelManip caching      |
| Jat15           | Various tweaks.                  |
| Jeija           | **Main developer! Everything.**  |
| Jordach         | Noteblock sounds.                |
| khonkhortistan  | Code, recipes, textures.         |
| Kotolegokot     | Nodeboxes for items.             |
| minerd247       | Textures.                        |
| Nore/Novatux    | Code.                            |
| RealBadAngel    | Fixes, improvements.             |
| sfan5           | Code, recipes, textures.         |
| suzenako        | Piston sounds.                   |
| Uberi/Temperest | Code, textures, documentation.   |
| VanessaE        | Code, recipes, textures, design. |
| Whiskers75      | Logic gates implementation.      |

There are also a whole bunch of other people helping with everything from code to testing and feedback. Mesecons would also not be possible without their help!

Alright, how can I use it?
--------------------------
All textures in this project are licensed under the CC-BY-SA 3.0 (Creative Commons Attribution-ShareAlike 3.0 Generic).
That means you can distribute and remix them as much as you want to, under the condition that you give credit to the authors and the project, and that if you remix and release them, they must be under the same or similar license to this one.

All code in this project is licensed under the LGPL version 3.
That means you have unlimited freedom to distribute and modify the work however you see fit, provided that if you decide to distribute it or any modified versions of it, you must also use the same license. The LGPL also grants the additional freedom to write extensions for the software and distribute them without the extensions being subject to the terms of the LGPL, although the software itself retains its license.

No warranty is provided, express or implied, for any part of the project.


mesecons

description: Mesecons is a mod for Minetest that implements items related to digital circuitry: wires, buttons, lights, and programmable controllers.

keywords: mesecons minetest mod


code:
--[[
Mesecons uses something it calls an ActionQueue.

The ActionQueue holds functions and actions.
Functions are added on load time with a specified name.
Actions are preserved over server restarts.

Each action consists of a position, the name of an added function to be called,
the params that should be used in this function call (additionally to the pos),
the time after which it should be executed, an optional overwritecheck and a
priority.

If time = 0, the action will be executed in the next globalstep, otherwise the
earliest globalstep when it will be executed is the after next globalstep.

It is guaranteed, that for two actions ac1, ac2 where ac1 ~= ac2,
ac1.time == ac2.time, ac1.priority == ac2.priority and ac1 was added earlier
than ac2, ac1 will be executed before ac2 (but in the same globalstep).

Note: Do not pass references in params, as they can not be preserved.

Also note: Some of the guarantees here might be dropped at some time.
]]

-- localize for speed
local queue = mesecon.queue

queue.actions = {} -- contains all ActionQueue actions

function queue:add_function(name, func)
	self.funcs[name] = func
end

-- If add_action with twice the same overwritecheck and same position are called, the first one is overwritten
-- use overwritecheck nil to never overwrite, but just add the event to the queue
-- priority specifies the order actions are executed within one globalstep, highest first
-- should be between 0 and 1
function queue:add_action(pos, func, params, time, overwritecheck, priority)
	-- Create Action Table:
	time = time or 0 -- time <= 0 --> execute, time > 0 --> wait time until execution
	priority = priority or 1
	local action = {
		pos = mesecon.tablecopy(pos),
		func = func,
		params = mesecon.tablecopy(params or {}),
		time = time,
		owcheck = (overwritecheck and mesecon.tablecopy(overwritecheck)) or nil,
		priority = priority
	}

	 -- check if old action has to be overwritten / removed:
	if overwritecheck then
		for i, ac in ipairs(self.actions) do
			if vector.equals(pos, ac.pos)
					and mesecon.cmpAny(overwritecheck, ac.owcheck) then
				-- remove the old action
				table.remove(self.actions, i)
				break
			end
		end
	end

	table.insert(self.actions, action)
end

-- execute the stored functions on a globalstep
-- if however, the pos of a function is not loaded (get_node_or_nil == nil), do NOT execute the function
-- this makes sure that resuming mesecons circuits when restarting minetest works fine (hm, where do we do this?)
-- However, even that does not work in some cases, that's why we delay the time the globalsteps
-- start to be execute by 4 seconds

local function globalstep_func(dtime)
	local actions = queue.actions
	-- split into two categories:
	-- actions_now: actions to execute now
	-- queue.actions: actions to execute later
	local actions_now = {}
	queue.actions = {}

	for _, ac in ipairs(actions) do
		if ac.time > 0 then
			-- action ac is to be executed later
			-- ~> insert into queue.actions
			ac.time = ac.time - dtime
			table.insert(queue.actions, ac)
		else
			-- action ac is to be executed now
			-- ~> insert into actions_now
			table.insert(actions_now, ac)
		end
	end

	-- stable-sort the executed actions after their priority
	-- some constructions might depend on the execution order, hence we first
	-- execute the actions that had a lower index in actions_now
	local old_action_order = {}
	for i, ac in ipairs(actions_now) do
		old_action_order[ac] = i
	end
	table.sort(actions_now, function(ac1, ac2)
		if ac1.priority ~= ac2.priority then
			return ac1.priority > ac2.priority
		else
			return old_action_order[ac1] < old_action_order[ac2]
		end
	end)

	-- execute highest priorities first, until all are executed
	for _, ac in ipairs(actions_now) do
		queue:execute(ac)
	end
end

-- delay the time the globalsteps start to be execute by 4 seconds
do
	local m_time = 0
	local resumetime = mesecon.setting("resumetime", 4)
	local globalstep_func_index = #minetest.registered_globalsteps + 1

	minetest.register_globalstep(function(dtime)
		m_time = m_time + dtime
		-- don't even try if server has not been running for XY seconds; resumetime = time to wait
		-- after starting the server before processing the ActionQueue, don't set this too low
		if m_time < resumetime then
			return
		end
		-- replace this globalstep function
		minetest.registered_globalsteps[globalstep_func_index] = globalstep_func
	end)
end

function queue:execute(action)
	-- ignore if action queue function name doesn't exist,
	-- (e.g. in case the action queue savegame was written by an old mesecons version)
	if self.funcs[action.func] then
		self.funcs[action.func](action.pos, unpack(action.params))
	end
end

-- Store and read the ActionQueue to / from a file
-- so that upcoming actions are remembered when the game
-- is restarted
queue.actions = mesecon.file2table("mesecon_actionqueue")

minetest.register_on_shutdown(function()
	mesecon.table2file("mesecon_actionqueue", queue.actions)
end)


code:
-- a simple first-in-first-out queue
-- very similar to the one in https://github.com/minetest/minetest/pull/7683

local fifo_queue = {}

local metatable = {__index = fifo_queue}

-- creates a new empty queue
function fifo_queue.new()
	local q = {n_in = 0, n_out = 0, i_out = 1, buf_in = {}, buf_out = {}}
	setmetatable(q, metatable)
	return q
end

-- adds an element to the queue
function fifo_queue.add(self, v)
	local n = self.n_in + 1
	self.n_in = n
	self.buf_in[n] = v
end

-- removes and returns the next element, or nil of empty
function fifo_queue.take(self)
	local i_out = self.i_out
	if i_out <= self.n_out then
		local v = self.buf_out[i_out]
		self.i_out = i_out + 1
		self.buf_out[i_out] = true
		return v
	end

	-- buf_out is empty, try to swap
	self.i_out = 1
	self.n_out = 0
	if self.n_in == 0 then
		return nil -- empty
	end

	-- swap
	self.n_out = self.n_in
	self.n_in = 0
	self.buf_out, self.buf_in = self.buf_in, self.buf_out

	local v = self.buf_out[1]
	self.i_out = 2
	self.buf_out[1] = true
	return v
end

-- returns whether the queue is empty
function fifo_queue.is_empty(self)
	return self.n_out == self.i_out + 1 and self.n_in == 0
end

-- returns stuff for iteration in a for loop, like pairs
-- adding new elements while iterating is no problem
function fifo_queue.iter(self)
	return fifo_queue.take, self, nil
end

return fifo_queue


code:
-- |\    /| ____ ____  ____ _____   ____         _____
-- | \  / | |    |     |    |      |    | |\   | |
-- |  \/  | |___ ____  |___ |      |    | | \  | |____
-- |      | |        | |    |      |    | |  \ |     |
-- |      | |___ ____| |___ |____  |____| |   \| ____|
-- by Jeija, Uberi (Temperest), sfan5, VanessaE, Hawk777 and contributors
--
--
--
-- This mod adds mesecons[=minecraft redstone] and different receptors/effectors to minetest.
-- See the documentation on the forum for additional information, especially about crafting
--
--
-- For basic development resources, see http://mesecons.net/developers.html
--
--
--
--Quick draft for the mesecons array in the node's definition
--mesecons =
--{
--	receptor =
--	{
--		state = mesecon.state.on/off
--		rules = rules/get_rules
--	},
--	effector =
--	{
--		action_on = function
--		action_off = function
--		action_change = function
--		rules = rules/get_rules
--	},
--	conductor =
--	{
--		state = mesecon.state.on/off
--		offstate = opposite state (for state = on only)
--		onstate = opposite state (for state = off only)
--		rules = rules/get_rules
--	}
--}

-- PUBLIC VARIABLES
mesecon={} -- contains all functions and all global variables
mesecon.queue={} -- contains the ActionQueue
mesecon.queue.funcs={} -- contains all ActionQueue functions

-- Settings
dofile(minetest.get_modpath("mesecons").."/settings.lua")

-- Utilities like comparing positions,
-- adding positions and rules,
-- mostly things that make the source look cleaner
dofile(minetest.get_modpath("mesecons").."/util.lua");

-- Presets (eg default rules)
dofile(minetest.get_modpath("mesecons").."/presets.lua");

-- The ActionQueue
-- Saves all the actions that have to be execute in the future
dofile(minetest.get_modpath("mesecons").."/actionqueue.lua");

-- Internal stuff
-- This is the most important file
-- it handles signal transmission and basically everything else
-- It is also responsible for managing the nodedef things,
-- like calling action_on/off/change
dofile(minetest.get_modpath("mesecons").."/internal.lua");

-- API
-- these are the only functions you need to remember

mesecon.queue:add_function("receptor_on", function (pos, rules)
	mesecon.vm_begin()

	rules = rules or mesecon.rules.default

	-- Call turnon on all linking positions
	for _, rule in ipairs(mesecon.flattenrules(rules)) do
		local np = vector.add(pos, rule)
		local rulenames = mesecon.rules_link_rule_all(pos, rule)
		for _, rulename in ipairs(rulenames) do
			mesecon.turnon(np, rulename)
		end
	end

	mesecon.vm_commit()
end)

function mesecon.receptor_on(pos, rules)
	mesecon.queue:add_action(pos, "receptor_on", {rules}, nil, rules)
end

mesecon.queue:add_function("receptor_off", function (pos, rules)
	rules = rules or mesecon.rules.default

	-- Call turnoff on all linking positions
	for _, rule in ipairs(mesecon.flattenrules(rules)) do
		local np = vector.add(pos, rule)
		local rulenames = mesecon.rules_link_rule_all(pos, rule)
		for _, rulename in ipairs(rulenames) do
			mesecon.vm_begin()
			mesecon.changesignal(np, minetest.get_node(np), rulename, mesecon.state.off, 2)

			-- Turnoff returns true if turnoff process was successful, no onstate receptor
			-- was found along the way. Commit changes that were made in voxelmanip. If turnoff
			-- returns true, an onstate receptor was found, abort voxelmanip transaction.
			if (mesecon.turnoff(np, rulename)) then
				mesecon.vm_commit()
			else
				mesecon.vm_abort()
			end
		end
	end
end)

function mesecon.receptor_off(pos, rules)
	mesecon.queue:add_action(pos, "receptor_off", {rules}, nil, rules)
end

print("[OK] Mesecons")

-- Deprecated stuff
-- To be removed in future releases
dofile(minetest.get_modpath("mesecons").."/legacy.lua");

--Services like turnoff receptor on dignode and so on
dofile(minetest.get_modpath("mesecons").."/services.lua");


code:
-- Internal.lua - The core of mesecons
--
-- For more practical developer resources see http://mesecons.net/developers.php
--
-- Function overview
-- mesecon.get_effector(nodename)	--> Returns the mesecons.effector -specifictation in the nodedef by the nodename
-- mesecon.get_receptor(nodename)	--> Returns the mesecons.receptor -specifictation in the nodedef by the nodename
-- mesecon.get_conductor(nodename)	--> Returns the mesecons.conductor-specifictation in the nodedef by the nodename
-- mesecon.get_any_inputrules (node)	--> Returns the rules of a node if it is a conductor or an effector
-- mesecon.get_any_outputrules (node)	--> Returns the rules of a node if it is a conductor or a receptor

-- RECEPTORS
-- mesecon.is_receptor(nodename)	--> Returns true if nodename is a receptor
-- mesecon.is_receptor_on(nodename)	--> Returns true if nodename is an receptor with state = mesecon.state.on
-- mesecon.is_receptor_off(nodename)	--> Returns true if nodename is an receptor with state = mesecon.state.off
-- mesecon.receptor_get_rules(node)	--> Returns the rules of the receptor (mesecon.rules.default if none specified)

-- EFFECTORS
-- mesecon.is_effector(nodename)	--> Returns true if nodename is an effector
-- mesecon.is_effector_on(nodename)	--> Returns true if nodename is an effector with nodedef.mesecons.effector.action_off
-- mesecon.is_effector_off(nodename)	--> Returns true if nodename is an effector with nodedef.mesecons.effector.action_on
-- mesecon.effector_get_rules(node)	--> Returns the input rules of the effector (mesecon.rules.default if none specified)

-- SIGNALS
-- mesecon.activate(pos, node, depth)				--> Activates   the effector node at the specific pos (calls nodedef.mesecons.effector.action_on), higher depths are executed later
-- mesecon.deactivate(pos, node, depth)				--> Deactivates the effector node at the specific pos (calls nodedef.mesecons.effector.action_off), higher depths are executed later
-- mesecon.changesignal(pos, node, rulename, newstate, depth)	--> Changes     the effector node at the specific pos (calls nodedef.mesecons.effector.action_change), higher depths are executed later

-- CONDUCTORS
-- mesecon.is_conductor(nodename)	--> Returns true if nodename is a conductor
-- mesecon.is_conductor_on(node		--> Returns true if node is a conductor with state = mesecon.state.on
-- mesecon.is_conductor_off(node)	--> Returns true if node is a conductor with state = mesecon.state.off
-- mesecon.get_conductor_on(node_off)	--> Returns the onstate  nodename of the conductor
-- mesecon.get_conductor_off(node_on)	--> Returns the offstate nodename of the conductor
-- mesecon.conductor_get_rules(node)	--> Returns the input+output rules of a conductor (mesecon.rules.default if none specified)

-- HIGH-LEVEL Internals
-- mesecon.is_power_on(pos)				--> Returns true if pos emits power in any way
-- mesecon.is_power_off(pos)				--> Returns true if pos does not emit power in any way
-- mesecon.is_powered(pos)				--> Returns true if pos is powered by a receptor or a conductor

-- RULES ROTATION helpers
-- mesecon.rotate_rules_right(rules)
-- mesecon.rotate_rules_left(rules)
-- mesecon.rotate_rules_up(rules)
-- mesecon.rotate_rules_down(rules)
-- These functions return rules that have been rotated in the specific direction

-- See fifo_queue.lua for documentation.
mesecon.fifo_queue = dofile(minetest.get_modpath("mesecons").."/fifo_queue.lua")

-- General
function mesecon.get_effector(nodename)
	if  minetest.registered_nodes[nodename]
	and minetest.registered_nodes[nodename].mesecons
	and minetest.registered_nodes[nodename].mesecons.effector then
		return minetest.registered_nodes[nodename].mesecons.effector
	end
end

function mesecon.get_receptor(nodename)
	if  minetest.registered_nodes[nodename]
	and minetest.registered_nodes[nodename].mesecons
	and minetest.registered_nodes[nodename].mesecons.receptor then
		return minetest.registered_nodes[nodename].mesecons.receptor
	end
end

function mesecon.get_conductor(nodename)
	if  minetest.registered_nodes[nodename]
	and minetest.registered_nodes[nodename].mesecons
	and minetest.registered_nodes[nodename].mesecons.conductor then
		return minetest.registered_nodes[nodename].mesecons.conductor
	end
end

function mesecon.get_any_outputrules(node)
	if not node then return nil end

	if mesecon.is_conductor(node.name) then
		return mesecon.conductor_get_rules(node)
	elseif mesecon.is_receptor(node.name) then
		return mesecon.receptor_get_rules(node)
	end
end

function mesecon.get_any_inputrules(node)
	if not node then return nil end

	if mesecon.is_conductor(node.name) then
		return mesecon.conductor_get_rules(node)
	elseif mesecon.is_effector(node.name) then
		return mesecon.effector_get_rules(node)
	end
end

function mesecon.get_any_rules(node)
	return mesecon.merge_rule_sets(mesecon.get_any_inputrules(node),
		mesecon.get_any_outputrules(node))
end

-- Receptors
-- Nodes that can power mesecons
function mesecon.is_receptor_on(nodename)
	local receptor = mesecon.get_receptor(nodename)
	if receptor and receptor.state == mesecon.state.on then
		return true
	end
	return false
end

function mesecon.is_receptor_off(nodename)
	local receptor = mesecon.get_receptor(nodename)
	if receptor and receptor.state == mesecon.state.off then
		return true
	end
	return false
end

function mesecon.is_receptor(nodename)
	local receptor = mesecon.get_receptor(nodename)
	if receptor then
		return true
	end
	return false
end

function mesecon.receptor_get_rules(node)
	local receptor = mesecon.get_receptor(node.name)
	if receptor then
		local rules = receptor.rules
		if type(rules) == 'function' then
			return rules(node)
		elseif rules then
			return rules
		end
	end

	return mesecon.rules.default
end

-- Effectors
-- Nodes that can be powered by mesecons
function mesecon.is_effector_on(nodename)
	local effector = mesecon.get_effector(nodename)
	if effector and effector.action_off then
		return true
	end
	return false
end

function mesecon.is_effector_off(nodename)
	local effector = mesecon.get_effector(nodename)
	if effector and effector.action_on then
		return true
	end
	return false
end

function mesecon.is_effector(nodename)
	local effector = mesecon.get_effector(nodename)
	if effector then
		return true
	end
	return false
end

function mesecon.effector_get_rules(node)
	local effector = mesecon.get_effector(node.name)
	if effector then
		local rules = effector.rules
		if type(rules) == 'function' then
			return rules(node)
		elseif rules then
			return rules
		end
	end
	return mesecon.rules.default
end

-- #######################
-- # Signals (effectors) #
-- #######################

-- Activation:
mesecon.queue:add_function("activate", function (pos, rulename)
	local node = mesecon.get_node_force(pos)
	if not node then return end

	local effector = mesecon.get_effector(node.name)

	if effector and effector.action_on then
		effector.action_on(pos, node, rulename)
	end
end)

function mesecon.activate(pos, node, rulename, depth)
	if rulename == nil then
		for _,rule in ipairs(mesecon.effector_get_rules(node)) do
			mesecon.activate(pos, node, rule, depth + 1)
		end
		return
	end
	mesecon.queue:add_action(pos, "activate", {rulename}, nil, rulename, 1 / depth)
end

-- Deactivation
mesecon.queue:add_function("deactivate", function (pos, rulename)
	local node = mesecon.get_node_force(pos)
	if not node then return end

	local effector = mesecon.get_effector(node.name)

	if effector and effector.action_off then
		effector.action_off(pos, node, rulename)
	end
end)

function mesecon.deactivate(pos, node, rulename, depth)
	if rulename == nil then
		for _,rule in ipairs(mesecon.effector_get_rules(node)) do
			mesecon.deactivate(pos, node, rule, depth + 1)
		end
		return
	end
	mesecon.queue:add_action(pos, "deactivate", {rulename}, nil, rulename, 1 / depth)
end

-- Change
mesecon.queue:add_function("change", function (pos, rulename, changetype)
	local node = mesecon.get_node_force(pos)
	if not node then return end

	local effector = mesecon.get_effector(node.name)

	if effector and effector.action_change then
		effector.action_change(pos, node, rulename, changetype)
	end
end)

function mesecon.changesignal(pos, node, rulename, newstate, depth)
	if rulename == nil then
		for _,rule in ipairs(mesecon.effector_get_rules(node)) do
			mesecon.changesignal(pos, node, rule, newstate, depth + 1)
		end
		return
	end

	-- Include "change" in overwritecheck so that it cannot be overwritten
	-- by "active" / "deactivate" that will be called upon the node at the same time.
	local overwritecheck = {"change", rulename}
	mesecon.queue:add_action(pos, "change", {rulename, newstate}, nil, overwritecheck, 1 / depth)
end

-- Conductors

function mesecon.is_conductor_on(node, rulename)
	if not node then return false end

	local conductor = mesecon.get_conductor(node.name)
	if conductor then
		if conductor.state then
			return conductor.state == mesecon.state.on
		end
		if conductor.states then
			if not rulename then
				return mesecon.getstate(node.name, conductor.states) ~= 1
			end
			local bit = mesecon.rule2bit(rulename, mesecon.conductor_get_rules(node))
			local binstate = mesecon.getbinstate(node.name, conductor.states)
			return mesecon.get_bit(binstate, bit)
		end
	end

	return false
end

function mesecon.is_conductor_off(node, rulename)
	if not node then return false end

	local conductor = mesecon.get_conductor(node.name)
	if conductor then
		if conductor.state then
			return conductor.state == mesecon.state.off
		end
		if conductor.states then
			if not rulename then
				return mesecon.getstate(node.name, conductor.states) == 1
			end
			local bit = mesecon.rule2bit(rulename, mesecon.conductor_get_rules(node))
			local binstate = mesecon.getbinstate(node.name, conductor.states)
			return not mesecon.get_bit(binstate, bit)
		end
	end

	return false
end

function mesecon.is_conductor(nodename)
	local conductor = mesecon.get_conductor(nodename)
	if conductor then
		return true
	end
	return false
end

function mesecon.get_conductor_on(node_off, rulename)
	local conductor = mesecon.get_conductor(node_off.name)
	if conductor then
		if conductor.onstate then
			return conductor.onstate
		end
		if conductor.states then
			local bit = mesecon.rule2bit(rulename, mesecon.conductor_get_rules(node_off))
			local binstate = mesecon.getbinstate(node_off.name, conductor.states)
			binstate = mesecon.set_bit(binstate, bit, "1")
			return conductor.states[tonumber(binstate,2)+1]
		end
	end
	return nil
end

function mesecon.get_conductor_off(node_on, rulename)
	local conductor = mesecon.get_conductor(node_on.name)
	if conductor then
		if conductor.offstate then
			return conductor.offstate
		end
		if conductor.states then
			local bit = mesecon.rule2bit(rulename, mesecon.conductor_get_rules(node_on))
			local binstate = mesecon.getbinstate(node_on.name, conductor.states)
			binstate = mesecon.set_bit(binstate, bit, "0")
			return conductor.states[tonumber(binstate,2)+1]
		end
	end
	return nil
end

function mesecon.conductor_get_rules(node)
	local conductor = mesecon.get_conductor(node.name)
	if conductor then
		local rules = conductor.rules
		if type(rules) == 'function' then
			return rules(node)
		elseif rules then
			return rules
		end
	end
	return mesecon.rules.default
end

-- some more general high-level stuff

function mesecon.is_power_on(pos, rulename)
	local node = mesecon.get_node_force(pos)
	if node and (mesecon.is_conductor_on(node, rulename) or mesecon.is_receptor_on(node.name)) then
		return true
	end
	return false
end

function mesecon.is_power_off(pos, rulename)
	local node = mesecon.get_node_force(pos)
	if node and (mesecon.is_conductor_off(node, rulename) or mesecon.is_receptor_off(node.name)) then
		return true
	end
	return false
end

-- The set of conductor states which require light updates when they change.
local light_update_conductors

-- Calculate the contents of the above set if they have not been calculated.
local function find_light_update_conductors()
	-- The expensive calculation is only done the first time.
	if light_update_conductors then return end

	light_update_conductors = {}

	-- Find conductors whose lighting characteristics change depending on their state.
	local checked = {}
	for name, def in pairs(minetest.registered_nodes) do
		local conductor = mesecon.get_conductor(name)
		if conductor and not checked[name] then
			-- Find the other states of the conductor besides the current one.
			local other_states
			if conductor.onstate then
				other_states = {conductor.onstate}
			elseif conductor.offstate then
				other_states = {conductor.offstate}
			else
				other_states = conductor.states
			end

			-- Check the conductor. Other states are marked as checked.
			for _, other_state in ipairs(other_states) do
				local other_def = minetest.registered_nodes[other_state]
				if (def.paramtype == "light") ~= (other_def.paramtype == "light")
				or def.sunlight_propagates ~= other_def.sunlight_propagates
				or def.light_source ~= other_def.light_source then
					-- The light characteristics change depending on the state.
					-- The states are added to the set.
					light_update_conductors[name] = true
					for _, other_state in ipairs(other_states) do
						light_update_conductors[other_state] = true
						checked[other_state] = true
					end
					break
				end
				checked[other_state] = true
			end
		end
	end
end

-- Turn off an equipotential section starting at `pos`, which outputs in the direction of `link`.
-- Breadth-first search. Map is abstracted away in a voxelmanip.
-- Follow all all conductor paths replacing conductors that were already
-- looked at, activating / changing all effectors along the way.
function mesecon.turnon(pos, link)
	find_light_update_conductors()

	local frontiers = mesecon.fifo_queue.new()
	frontiers:add({pos = pos, link = link})
	local pos_can_be_skipped = {}

	local depth = 1
	for f in frontiers:iter() do
		local node = mesecon.get_node_force(f.pos)

		if not node then
			-- Area does not exist; do nothing
			pos_can_be_skipped[minetest.hash_node_position(f.pos)] = true
		elseif mesecon.is_conductor(node.name) then
			local rules = mesecon.conductor_get_rules(node)

			if mesecon.is_conductor_off(node, f.link) then
				-- Call turnon on neighbors
				for _, r in ipairs(mesecon.rule2meta(f.link, rules)) do
					local np = vector.add(f.pos, r)
					if not pos_can_be_skipped[minetest.hash_node_position(np)] then
						for _, l in ipairs(mesecon.rules_link_rule_all(f.pos, r)) do
							frontiers:add({pos = np, link = l})
						end
					end
				end

				mesecon.swap_node_force(f.pos, mesecon.get_conductor_on(node, f.link), light_update_conductors[node.name] ~= nil)
			end

			-- Only conductors with flat rules can be reliably skipped later
			if not rules[1] or rules[1].x then
				pos_can_be_skipped[minetest.hash_node_position(f.pos)] = true
			end
		elseif mesecon.is_effector(node.name) then
			mesecon.changesignal(f.pos, node, f.link, mesecon.state.on, depth)
			if mesecon.is_effector_off(node.name) then
				mesecon.activate(f.pos, node, f.link, depth)
			end
		else
			pos_can_be_skipped[minetest.hash_node_position(f.pos)] = true
		end
		depth = depth + 1
	end
end

-- Turn on an equipotential section starting at `pos`, which outputs in the direction of `link`.
-- Breadth-first search. Map is abstracted away in a voxelmanip.
-- Follow all all conductor paths replacing conductors that were already
-- looked at, deactivating / changing all effectors along the way.
-- In case an onstate receptor is discovered, abort the process by returning false, which will
-- cause `receptor_off` to discard all changes made in the voxelmanip.
-- Contrary to turnon, turnoff has to cache all change and deactivate signals so that they will only
-- be called in the very end when we can be sure that no conductor was found along the path.
--
-- Signal table entry structure:
-- {
--	pos = position of effector,
--	node = node descriptor (name, param1 and param2),
--	link = link the effector is connected to,
--	depth = indicates order in which signals wire fired, higher is later
-- }
function mesecon.turnoff(pos, link)
	find_light_update_conductors()

	local frontiers = mesecon.fifo_queue.new()
	frontiers:add({pos = pos, link = link})
	local signals = {}
	local pos_can_be_skipped = {}

	local depth = 1
	for f in frontiers:iter() do
		local node = mesecon.get_node_force(f.pos)

		if not node then
			-- Area does not exist; do nothing
			pos_can_be_skipped[minetest.hash_node_position(f.pos)] = true
		elseif mesecon.is_conductor(node.name) then
			local rules = mesecon.conductor_get_rules(node)

			if mesecon.is_conductor_on(node, f.link) then
				for _, r in ipairs(mesecon.rule2meta(f.link, rules)) do
					local np = vector.add(f.pos, r)

					if not pos_can_be_skipped[minetest.hash_node_position(np)] then
						-- Check if an onstate receptor is connected. If that is the case,
						-- abort this turnoff process by returning false. `receptor_off` will
						-- discard all the changes that we made in the voxelmanip:
						if mesecon.rules_link_rule_all_inverted(f.pos, r)[1] then
							if mesecon.is_receptor_on(mesecon.get_node_force(np).name) then
								return false
							end
						end

						-- Call turnoff on neighbors
						for _, l in ipairs(mesecon.rules_link_rule_all(f.pos, r)) do
							frontiers:add({pos = np, link = l})
						end
					end
				end

				mesecon.swap_node_force(f.pos, mesecon.get_conductor_off(node, f.link), light_update_conductors[node.name] ~= nil)
			end

			-- Only conductors with flat rules can be reliably skipped later
			if not rules[1] or rules[1].x then
				pos_can_be_skipped[minetest.hash_node_position(f.pos)] = true
			end
		elseif mesecon.is_effector(node.name) then
			table.insert(signals, {
				pos = f.pos,
				node = node,
				link = f.link,
				depth = depth
			})
		else
			pos_can_be_skipped[minetest.hash_node_position(f.pos)] = true
		end
		depth = depth + 1
	end

	for _, sig in ipairs(signals) do
		mesecon.changesignal(sig.pos, sig.node, sig.link, mesecon.state.off, sig.depth)
		if mesecon.is_effector_on(sig.node.name) and not mesecon.is_powered(sig.pos) then
			mesecon.deactivate(sig.pos, sig.node, sig.link, sig.depth)
		end
	end

	return true
end

-- Get all linking inputrules of inputnode (effector or conductor) that is connected to
-- outputnode (receptor or conductor) at position `output` and has an output in direction `rule`
function mesecon.rules_link_rule_all(output, rule)
	local input = vector.add(output, rule)
	local inputnode = mesecon.get_node_force(input)
	local inputrules = mesecon.get_any_inputrules(inputnode)
	if not inputrules then
		return {}
	end
	local rules = {}

	for _, inputrule in ipairs(mesecon.flattenrules(inputrules)) do
		-- Check if input accepts from output
		if  vector.equals(vector.add(input, inputrule), output) then
			table.insert(rules, inputrule)
		end
	end

	return rules
end

-- Get all linking outputnodes of outputnode (receptor or conductor) that is connected to
-- inputnode (effector or conductor) at position `input` and has an input in direction `rule`
function mesecon.rules_link_rule_all_inverted(input, rule)
	local output = vector.add(input, rule)
	local outputnode = mesecon.get_node_force(output)
	local outputrules = mesecon.get_any_outputrules(outputnode)
	if not outputrules then
		return {}
	end
	local rules = {}

	for _, outputrule in ipairs(mesecon.flattenrules(outputrules)) do
		if  vector.equals(vector.add(output, outputrule), input) then
			table.insert(rules, mesecon.invertRule(outputrule))
		end
	end
	return rules
end

function mesecon.is_powered(pos, rule)
	local node = mesecon.get_node_force(pos)
	local rules = mesecon.get_any_inputrules(node)
	if not rules then return false end

	-- List of nodes that send out power to pos
	local sourcepos = {}

	if not rule then
		for _, rule in ipairs(mesecon.flattenrules(rules)) do
			local rulenames = mesecon.rules_link_rule_all_inverted(pos, rule)
			for _, rname in ipairs(rulenames) do
				local np = vector.add(pos, rname)
				local nn = mesecon.get_node_force(np)

				if (mesecon.is_conductor_on(nn, mesecon.invertRule(rname))
				or mesecon.is_receptor_on(nn.name)) then
					table.insert(sourcepos, np)
				end
			end
		end
	else
		local rulenames = mesecon.rules_link_rule_all_inverted(pos, rule)
		for _, rname in ipairs(rulenames) do
			local np = vector.add(pos, rname)
			local nn = mesecon.get_node_force(np)
			if (mesecon.is_conductor_on (nn, mesecon.invertRule(rname))
			or mesecon.is_receptor_on (nn.name)) then
				table.insert(sourcepos, np)
			end
		end
	end

	-- Return FALSE if not powered, return list of sources if is powered
	if (#sourcepos == 0) then return false
	else return sourcepos end
end


code:
-- Un-forceload any forceloaded mapblocks from older versions of Mesecons which
-- used forceloading instead of VoxelManipulators.
local BLOCKSIZE = 16

-- convert block hash --> node position
local function unhash_blockpos(hash)
	return vector.multiply(minetest.get_position_from_hash(hash), BLOCKSIZE)
end

local old_forceloaded_blocks = mesecon.file2table("mesecon_forceloaded")
for hash, _ in pairs(old_forceloaded_blocks) do
	minetest.forceload_free_block(unhash_blockpos(hash))
end
os.remove(minetest.get_worldpath().."/mesecon_forceloaded")


code:
name = mesecons
# default is an optional dependency as some mods may expect it as a transitory
# dependency when they depend on mesecons.
optional_depends = default


code:
minetest.register_node("mesecons:mesecon_off", {
	drawtype = "raillike",
	tiles = {"jeija_mesecon_off.png", "jeija_mesecon_curved_off.png", "jeija_mesecon_t_junction_off.png", "jeija_mesecon_crossing_off.png"},
	inventory_image = "jeija_mesecon_off.png",
	wield_image = "jeija_mesecon_off.png",
	paramtype = "light",
	is_ground_content = false,
	walkable = false,
	selection_box = {
		type = "fixed",
		fixed = {-0.5, -0.5, -0.5, 0.5, -0.45, 0.5},
	},
	groups = {dig_immediate=3, mesecon=1, mesecon_conductor_craftable=1},
	description="Mesecons",
	mesecons = {conductor={
		state = mesecon.state.off,
		onstate = "mesecons:mesecon_on"
	}}
})

minetest.register_node("mesecons:mesecon_on", {
	drawtype = "raillike",
	tiles = {"jeija_mesecon_on.png", "jeija_mesecon_curved_on.png", "jeija_mesecon_t_junction_on.png", "jeija_mesecon_crossing_on.png"},
	paramtype = "light",
	is_ground_content = false,
	walkable = false,
	selection_box = {
		type = "fixed",
		fixed = {-0.5, -0.5, -0.5, 0.5, -0.45, 0.5},
	},
	groups = {dig_immediate=3, not_in_creaive_inventory=1, mesecon=1},
	drop = "mesecons:mesecon_off 1",
	light_source = minetest.LIGHT_MAX-11,
	mesecons = {conductor={
		state = mesecon.state.on,
		offstate = "mesecons:mesecon_off"
	}}
})


code:
mesecon.rules = {}
mesecon.state = {}

mesecon.rules.default = {
	{x =  0, y =  0, z = -1},
	{x =  1, y =  0, z =  0},
	{x = -1, y =  0, z =  0},
	{x =  0, y =  0, z =  1},
	{x =  1, y =  1, z =  0},
	{x =  1, y = -1, z =  0},
	{x = -1, y =  1, z =  0},
	{x = -1, y = -1, z =  0},
	{x =  0, y =  1, z =  1},
	{x =  0, y = -1, z =  1},
	{x =  0, y =  1, z = -1},
	{x =  0, y = -1, z = -1},
}

mesecon.rules.floor = mesecon.merge_rule_sets(mesecon.rules.default, {{x = 0, y = -1, z = 0}})

mesecon.rules.pplate = mesecon.merge_rule_sets(mesecon.rules.floor, {{x = 0, y = -2, z = 0}})

mesecon.rules.buttonlike = {
	{x = 1,  y =  0, z =  0},
	{x = 1,  y =  1, z =  0},
	{x = 1,  y = -1, z =  0},
	{x = 1,  y = -1, z =  1},
	{x = 1,  y = -1, z = -1},
	{x = 2,  y =  0, z =  0},
}

mesecon.rules.flat = {
	{x =  1, y = 0, z =  0},
	{x = -1, y = 0, z =  0},
	{x =  0, y = 0, z =  1},
	{x =  0, y = 0, z = -1},
}

mesecon.rules.alldirs = {
	{x =  1, y =  0,  z =  0},
	{x = -1, y =  0,  z =  0},
	{x =  0, y =  1,  z =  0},
	{x =  0, y = -1,  z =  0},
	{x =  0, y =  0,  z =  1},
	{x =  0, y =  0,  z = -1},
}

local rules_wallmounted = {
	xp = mesecon.rotate_rules_down(mesecon.rules.floor),
	xn = mesecon.rotate_rules_up(mesecon.rules.floor),
	yp = mesecon.rotate_rules_up(mesecon.rotate_rules_up(mesecon.rules.floor)),
	yn = mesecon.rules.floor,
	zp = mesecon.rotate_rules_left(mesecon.rotate_rules_up(mesecon.rules.floor)),
	zn = mesecon.rotate_rules_right(mesecon.rotate_rules_up(mesecon.rules.floor)),
}

local rules_buttonlike = {
	xp = mesecon.rules.buttonlike,
	xn = mesecon.rotate_rules_right(mesecon.rotate_rules_right(mesecon.rules.buttonlike)),
	yp = mesecon.rotate_rules_down(mesecon.rules.buttonlike),
	yn = mesecon.rotate_rules_up(mesecon.rules.buttonlike),
	zp = mesecon.rotate_rules_right(mesecon.rules.buttonlike),
	zn = mesecon.rotate_rules_left(mesecon.rules.buttonlike),
}

local function rules_from_dir(ruleset, dir)
	if dir.x ==  1 then return ruleset.xp end
	if dir.y ==  1 then return ruleset.yp end
	if dir.z ==  1 then return ruleset.zp end
	if dir.x == -1 then return ruleset.xn end
	if dir.y == -1 then return ruleset.yn end
	if dir.z == -1 then return ruleset.zn end
end

mesecon.rules.wallmounted_get = function(node)
	local dir = minetest.wallmounted_to_dir(node.param2)
	return rules_from_dir(rules_wallmounted, dir)
end

mesecon.rules.buttonlike_get = function(node)
	local dir = minetest.facedir_to_dir(node.param2)
	return rules_from_dir(rules_buttonlike, dir)
end

mesecon.state.on = "on"
mesecon.state.off = "off"


code:
-- Dig and place services

mesecon.on_placenode = function(pos, node)
	mesecon.execute_autoconnect_hooks_now(pos, node)
	node = minetest.get_node(pos) -- Update the node in case it was just changed.

	-- Receptors: Send on signal when active
	if mesecon.is_receptor_on(node.name) then
		mesecon.receptor_on(pos, mesecon.receptor_get_rules(node))
	end

	-- Conductors: Send turnon signal when powered or replace by respective offstate conductor
	-- if placed conductor is an onstate one
	if mesecon.is_conductor(node.name) then
		local conductor = mesecon.get_conductor(node.name)
		if conductor.state ~= mesecon.state.off then
			-- Turn the conductor off.
			node.name = conductor.offstate or conductor.states[1]
			minetest.swap_node(pos, node)
		end
		local sources = mesecon.is_powered(pos)
		if sources then
			mesecon.vm_begin()
			for _, s in ipairs(sources) do
				local rule = vector.subtract(s, pos)
				mesecon.turnon(pos, rule)
			end
			mesecon.vm_commit()
		end
	end

	-- Effectors: Send changesignal and activate or deactivate
	if mesecon.is_effector(node.name) then
		local powered_rules = {}
		local unpowered_rules = {}

		-- for each input rule, check if powered
		for _, r in ipairs(mesecon.effector_get_rules(node)) do
			local powered = mesecon.is_powered(pos, r)
			if powered then table.insert(powered_rules, r)
			else table.insert(unpowered_rules, r) end

			local state = powered and mesecon.state.on or mesecon.state.off
			mesecon.changesignal(pos, node, r, state, 1)
		end

		if (#powered_rules > 0) then
			for _, r in ipairs(powered_rules) do
				mesecon.activate(pos, node, r, 1)
			end
		else
			for _, r in ipairs(unpowered_rules) do
				mesecon.deactivate(pos, node, r, 1)
			end
		end
	end
end

mesecon.on_dignode = function(pos, node)
	if mesecon.is_conductor_on(node) then
		mesecon.receptor_off(pos, mesecon.conductor_get_rules(node))
	elseif mesecon.is_receptor_on(node.name) then
		mesecon.receptor_off(pos, mesecon.receptor_get_rules(node))
	end

	mesecon.execute_autoconnect_hooks_queue(pos, node)
end

function mesecon.on_blastnode(pos)
	local node = minetest.get_node(pos)
	minetest.remove_node(pos)
	mesecon.on_dignode(pos, node)
	return minetest.get_node_drops(node.name, "")
end

minetest.register_on_placenode(mesecon.on_placenode)
minetest.register_on_dignode(mesecon.on_dignode)

-- Overheating service for fast circuits
local OVERHEAT_MAX = mesecon.setting("overheat_max", 20)
local COOLDOWN_TIME = mesecon.setting("cooldown_time", 2.0)
local COOLDOWN_STEP = mesecon.setting("cooldown_granularity", 0.5)
local COOLDOWN_MULTIPLIER = OVERHEAT_MAX / COOLDOWN_TIME
local cooldown_timer = 0.0
local object_heat = {}

-- returns true if heat is too high
function mesecon.do_overheat(pos)
	local id = minetest.hash_node_position(pos)
	local heat = (object_heat[id] or 0) + 1
	object_heat[id] = heat
	if heat >= OVERHEAT_MAX then
		minetest.log("action", "Node overheats at " .. minetest.pos_to_string(pos))
		object_heat[id] = nil
		return true
	end
	return false
end

function mesecon.do_cooldown(pos)
	local id = minetest.hash_node_position(pos)
	object_heat[id] = nil
end

function mesecon.get_heat(pos)
	local id = minetest.hash_node_position(pos)
	return object_heat[id] or 0
end

function mesecon.move_hot_nodes(moved_nodes)
	local new_heat = {}
	for _, n in ipairs(moved_nodes) do
		local old_id = minetest.hash_node_position(n.oldpos)
		local new_id = minetest.hash_node_position(n.pos)
		new_heat[new_id] = object_heat[old_id]
		object_heat[old_id] = nil
	end
	for id, heat in pairs(new_heat) do
		object_heat[id] = heat
	end
end

local function global_cooldown(dtime)
	cooldown_timer = cooldown_timer + dtime
	if cooldown_timer < COOLDOWN_STEP then
		return -- don't overload the CPU
	end
	local cooldown = COOLDOWN_MULTIPLIER * cooldown_timer
	cooldown_timer = 0
	for id, heat in pairs(object_heat) do
		heat = heat - cooldown
		if heat <= 0 then
			object_heat[id] = nil -- free some RAM
		else
			object_heat[id] = heat
		end
	end
end
minetest.register_globalstep(global_cooldown)


code:
-- SETTINGS
function mesecon.setting(setting, default)
	if type(default) == "boolean" then
		local read = minetest.settings:get_bool("mesecon."..setting)
		if read == nil then
			return default
		else
			return read
		end
	elseif type(default) == "string" then
		return minetest.settings:get("mesecon."..setting) or default
	elseif type(default) == "number" then
		return tonumber(minetest.settings:get("mesecon."..setting) or default)
	end
end


code:
function mesecon.move_node(pos, newpos)
	local node = minetest.get_node(pos)
	local meta = minetest.get_meta(pos):to_table()
	minetest.remove_node(pos)
	minetest.set_node(newpos, node)
	minetest.get_meta(pos):from_table(meta)
end

-- An on_rotate callback for mesecons components.
function mesecon.on_rotate(pos, node, _, _, new_param2)
	local new_node = {name = node.name, param1 = node.param1, param2 = new_param2}
	minetest.swap_node(pos, new_node)
	mesecon.on_dignode(pos, node)
	mesecon.on_placenode(pos, new_node)
	minetest.check_for_falling(pos)
	return true
end

-- An on_rotate callback for components which stay horizontal.
function mesecon.on_rotate_horiz(pos, node, user, mode, new_param2)
	if not minetest.global_exists("screwdriver") or mode ~= screwdriver.ROTATE_FACE then
		return false
	end
	return mesecon.on_rotate(pos, node, user, mode, new_param2)
end

-- Rules rotation Functions:
function mesecon.rotate_rules_right(rules)
	local nr = {}
	for i, rule in ipairs(rules) do
		table.insert(nr, {
			x = -rule.z,
			y =  rule.y,
			z =  rule.x,
			name = rule.name})
	end
	return nr
end

function mesecon.rotate_rules_left(rules)
	local nr = {}
	for i, rule in ipairs(rules) do
		table.insert(nr, {
			x =  rule.z,
			y =  rule.y,
			z = -rule.x,
			name = rule.name})
	end
	return nr
end

function mesecon.rotate_rules_down(rules)
	local nr = {}
	for i, rule in ipairs(rules) do
		table.insert(nr, {
			x = -rule.y,
			y =  rule.x,
			z =  rule.z,
			name = rule.name})
	end
	return nr
end

function mesecon.rotate_rules_up(rules)
	local nr = {}
	for i, rule in ipairs(rules) do
		table.insert(nr, {
			x =  rule.y,
			y = -rule.x,
			z =  rule.z,
			name = rule.name})
	end
	return nr
end

-- Returns a rules getter function that returns different rules depending on the node's horizontal rotation.
-- If param2 % 4 == 0, then the rules returned by the getter are a copy of base_rules.
function mesecon.horiz_rules_getter(base_rules)
	local rotations = {mesecon.tablecopy(base_rules)}
	for i = 2, 4 do
		local right_rules = rotations[i - 1]
		if not right_rules[1] or right_rules[1].x then
			-- flat rules
			rotations[i] = mesecon.rotate_rules_left(right_rules)
		else
			-- not flat
			rotations[i] = {}
			for j, rules in ipairs(right_rules) do
				rotations[i][j] = mesecon.rotate_rules_left(rules)
			end
		end
	end
	return function(node)
		return rotations[node.param2 % 4 + 1]
	end
end

function mesecon.flattenrules(allrules)
--[[
	{
		{
			{xyz},
			{xyz},
		},
		{
			{xyz},
			{xyz},
		},
	}
--]]
	if allrules[1] and
	   allrules[1].x then
		return allrules
	end

	local shallowrules = {}
	for _, metarule in ipairs( allrules) do
	for _,     rule in ipairs(metarule ) do
		table.insert(shallowrules, rule)
	end
	end
	return shallowrules
--[[
	{
		{xyz},
		{xyz},
		{xyz},
		{xyz},
	}
--]]
end

function mesecon.rule2bit(findrule, allrules)
	--get the bit of the metarule the rule is in, or bit 1
	if (allrules[1] and
	    allrules[1].x) or
	    not findrule then
		return 1
	end
	for m,metarule in ipairs( allrules) do
	for _,    rule in ipairs(metarule ) do
		if vector.equals(findrule, rule) then
			return m
		end
	end
	end
end

function mesecon.rule2metaindex(findrule, allrules)
	--get the metarule the rule is in, or allrules
	if allrules[1].x then
		return nil
	end

	if not(findrule) then
		return mesecon.flattenrules(allrules)
	end

	for m, metarule in ipairs( allrules) do
	for _,     rule in ipairs(metarule ) do
		if vector.equals(findrule, rule) then
			return m
		end
	end
	end
end

function mesecon.rule2meta(findrule, allrules)
	if #allrules == 0 then return {} end

	local index = mesecon.rule2metaindex(findrule, allrules)
	if index == nil then
		if allrules[1].x then
			return allrules
		else
			return {}
		end
	end
	return allrules[index]
end

function mesecon.dec2bin(n)
	local x, y = math.floor(n / 2), n % 2
	if (n > 1) then
		return mesecon.dec2bin(x)..y
	else
		return ""..y
	end
end

function mesecon.getstate(nodename, states)
	for state, name in ipairs(states) do
		if name == nodename then
			return state
		end
	end
	error(nodename.." doesn't mention itself in "..dump(states))
end

function mesecon.getbinstate(nodename, states)
	return mesecon.dec2bin(mesecon.getstate(nodename, states)-1)
end

function mesecon.get_bit(binary,bit)
	bit = bit or 1
	local len = binary:len()
	if bit > len then return false end
	local c = len-(bit-1)
	return binary:sub(c,c) == "1"
end

function mesecon.set_bit(binary,bit,value)
	if value == "1" then
		if not mesecon.get_bit(binary,bit) then
			return mesecon.dec2bin(tonumber(binary,2)+math.pow(2,bit-1))
		end
	elseif value == "0" then
		if mesecon.get_bit(binary,bit) then
			return mesecon.dec2bin(tonumber(binary,2)-math.pow(2,bit-1))
		end
	end
	return binary

end

function mesecon.invertRule(r)
	return vector.multiply(r, -1)
end

function mesecon.tablecopy(obj) -- deep copy
	if type(obj) == "table" then
		return table.copy(obj)
	end
	return obj
end

-- Returns whether two values are equal.
-- In tables, keys are compared for identity but values are compared recursively.
-- There is no protection from infinite recursion.
function mesecon.cmpAny(t1, t2)
	if type(t1) ~= type(t2) then return false end
	if type(t1) ~= "table" then return t1 == t2 end

	-- Check that for each key of `t1` both tables have the same value
	for i, e in pairs(t1) do
		if not mesecon.cmpAny(e, t2[i]) then return false end
	end

	-- Check that all keys of `t2` are also keys of `t1` so were checked in the previous loop
	for i, _ in pairs(t2) do
		if t1[i] == nil then return false end
	end

	return true
end

-- Deprecated. Use `merge_tables` or `merge_rule_sets` as appropriate.
function mesecon.mergetable(source, dest)
	minetest.log("warning", debug.traceback("Deprecated call to mesecon.mergetable"))
	local rval = mesecon.tablecopy(dest)

	for k, v in pairs(source) do
		rval[k] = dest[k] or mesecon.tablecopy(v)
	end
	for i, v in ipairs(source) do
		table.insert(rval, mesecon.tablecopy(v))
	end

	return rval
end

-- Merges several rule sets in one. Order may not be preserved. Nil arguments
-- are ignored.
-- The rule sets must be of the same kind (either all single-level or all two-level).
-- The function may be changed to normalize the resulting set in some way.
function mesecon.merge_rule_sets(...)
	local rval = {}
	for _, t in pairs({...}) do -- ignores nils automatically
		table.insert_all(rval, mesecon.tablecopy(t))
	end
	return rval
end

-- Merges two tables, with entries from `replacements` taking precedence over
-- those from `base`. Returns the new table.
-- Values are deep-copied from either table, keys are referenced.
-- Numerical indices aren’t handled specially.
function mesecon.merge_tables(base, replacements)
	local ret = mesecon.tablecopy(replacements) -- these are never overriden so have to be copied in any case
	for k, v in pairs(base) do
		if ret[k] == nil then -- it could be `false`
			ret[k] = mesecon.tablecopy(v)
		end
	end
	return ret
end

function mesecon.register_node(name, spec_common, spec_off, spec_on)
	spec_common.drop = spec_common.drop or name .. "_off"
	spec_common.on_blast = spec_common.on_blast or mesecon.on_blastnode
	spec_common.__mesecon_basename = name
	spec_on.__mesecon_state = "on"
	spec_off.__mesecon_state = "off"

	spec_on = mesecon.merge_tables(spec_common, spec_on);
	spec_off = mesecon.merge_tables(spec_common, spec_off);

	minetest.register_node(name .. "_on", spec_on)
	minetest.register_node(name .. "_off", spec_off)
end

-- swap onstate and offstate nodes, returns new state
function mesecon.flipstate(pos, node)
	local nodedef = minetest.registered_nodes[node.name]
	local newstate
	if (nodedef.__mesecon_state == "on") then newstate = "off" end
	if (nodedef.__mesecon_state == "off") then newstate = "on" end

	minetest.swap_node(pos, {name = nodedef.__mesecon_basename .. "_" .. newstate,
		param2 = node.param2})

	return newstate
end

-- File writing / reading utilities
local wpath = minetest.get_worldpath()
function mesecon.file2table(filename)
	local f = io.open(wpath.."/"..filename, "r")
	if f == nil then return {} end
	local t = f:read("*all")
	f:close()
	if t == "" or t == nil then return {} end
	return minetest.deserialize(t)
end

function mesecon.table2file(filename, table)
	local f = io.open(wpath.."/"..filename, "w")
	f:write(minetest.serialize(table))
	f:close()
end

-- Block position "hashing" (convert to integer) functions for voxelmanip cache
local BLOCKSIZE = 16

-- convert node position --> block hash
local function hash_blockpos(pos)
	return minetest.hash_node_position({
		x = math.floor(pos.x/BLOCKSIZE),
		y = math.floor(pos.y/BLOCKSIZE),
		z = math.floor(pos.z/BLOCKSIZE)
	})
end

-- Maps from a hashed mapblock position (as returned by hash_blockpos) to a
-- table.
--
-- Contents of the table are:
-- “vm” → the VoxelManipulator
-- “dirty” → true if data has been modified
--
-- Nil if no VM-based transaction is in progress.
local vm_cache = nil

-- Cache from node position hashes to nodes (represented as tables).
local vm_node_cache = nil

-- Whether the current transaction will need a light update afterward.
local vm_update_light = false

-- Starts a VoxelManipulator-based transaction.
--
-- During a VM transaction, calls to vm_get_node and vm_swap_node operate on a
-- cached copy of the world loaded via VoxelManipulators. That cache can later
-- be committed to the real map by means of vm_commit or discarded by means of
-- vm_abort.
function mesecon.vm_begin()
	vm_cache = {}
	vm_node_cache = {}
	vm_update_light = false
end

-- Finishes a VoxelManipulator-based transaction, freeing the VMs and map data
-- and writing back any modified areas.
function mesecon.vm_commit()
	for hash, tbl in pairs(vm_cache) do
		if tbl.dirty then
			local vm = tbl.vm
			vm:write_to_map(vm_update_light)
			vm:update_map()
		end
	end
	vm_cache = nil
	vm_node_cache = nil
end

-- Finishes a VoxelManipulator-based transaction, freeing the VMs and throwing
-- away any modified areas.
function mesecon.vm_abort()
	vm_cache = nil
	vm_node_cache = nil
end

-- Gets the cache entry covering a position, populating it if necessary.
local function vm_get_or_create_entry(pos)
	local hash = hash_blockpos(pos)
	local tbl = vm_cache[hash]
	if not tbl then
		tbl = {vm = minetest.get_voxel_manip(pos, pos), dirty = false}
		vm_cache[hash] = tbl
	end
	return tbl
end

-- Gets the node at a given position during a VoxelManipulator-based
-- transaction.
function mesecon.vm_get_node(pos)
	local hash = minetest.hash_node_position(pos)
	local node = vm_node_cache[hash]
	if not node then
		node = vm_get_or_create_entry(pos).vm:get_node_at(pos)
		vm_node_cache[hash] = node
	end
	return node.name ~= "ignore" and {name = node.name, param1 = node.param1, param2 = node.param2} or nil
end

-- Sets a node’s name during a VoxelManipulator-based transaction.
--
-- Existing param1, param2, and metadata are left alone.
--
-- The swap will necessitate a light update unless update_light equals false.
function mesecon.vm_swap_node(pos, name, update_light)
	-- If one node needs a light update, all VMs should use light updates to
	-- prevent newly calculated light from being overwritten by other VMs.
	vm_update_light = vm_update_light or update_light ~= false

	local tbl = vm_get_or_create_entry(pos)
	local hash = minetest.hash_node_position(pos)
	local node = vm_node_cache[hash]
	if not node then
		node = tbl.vm:get_node_at(pos)
		vm_node_cache[hash] = node
	end
	node.name = name
	tbl.vm:set_node_at(pos, node)
	tbl.dirty = true
end

-- Gets the node at a given position, regardless of whether it is loaded or
-- not, respecting a transaction if one is in progress.
--
-- Outside a VM transaction, if the mapblock is not loaded, it is pulled into
-- the server’s main map data cache and then accessed from there.
--
-- Inside a VM transaction, the transaction’s VM cache is used.
function mesecon.get_node_force(pos)
	if vm_cache then
		return mesecon.vm_get_node(pos)
	else
		local node = minetest.get_node_or_nil(pos)
		if node == nil then
			-- Node is not currently loaded; use a VoxelManipulator to prime
			-- the mapblock cache and try again.
			minetest.get_voxel_manip(pos, pos)
			node = minetest.get_node_or_nil(pos)
		end
		return node
	end
end

-- Swaps the node at a given position, regardless of whether it is loaded or
-- not, respecting a transaction if one is in progress.
--
-- Outside a VM transaction, if the mapblock is not loaded, it is pulled into
-- the server’s main map data cache and then accessed from there.
--
-- Inside a VM transaction, the transaction’s VM cache is used.
--
-- This function can only be used to change the node’s name, not its parameters
-- or metadata.
--
-- The swap will necessitate a light update unless update_light equals false.
function mesecon.swap_node_force(pos, name, update_light)
	if vm_cache then
		return mesecon.vm_swap_node(pos, name, update_light)
	else
		-- This serves to both ensure the mapblock is loaded and also hand us
		-- the old node table so we can preserve param2.
		local node = mesecon.get_node_force(pos)
		node.name = name
		minetest.swap_node(pos, node)
	end
end

-- Autoconnect Hooks
-- Nodes like conductors may change their appearance and their connection rules
-- right after being placed or after being dug, e.g. the default wires use this
-- to automatically connect to linking nodes after placement.
-- After placement, the update function will be executed immediately so that the
-- possibly changed rules can be taken into account when recalculating the circuit.
-- After digging, the update function will be queued and executed after
-- recalculating the circuit. The update function must take care of updating the
-- node at the given position itself, but also all of the other nodes the given
-- position may have (had) a linking connection to.
mesecon.autoconnect_hooks = {}

-- name: A unique name for the hook, e.g. "foowire". Used to name the actionqueue function.
-- fct: The update function with parameters function(pos, node)
function mesecon.register_autoconnect_hook(name, fct)
	mesecon.autoconnect_hooks[name] = fct
	mesecon.queue:add_function("autoconnect_hook_"..name, fct)
end

function mesecon.execute_autoconnect_hooks_now(pos, node)
	for _, fct in pairs(mesecon.autoconnect_hooks) do
		fct(pos, node)
	end
end

function mesecon.execute_autoconnect_hooks_queue(pos, node)
	for name in pairs(mesecon.autoconnect_hooks) do
		mesecon.queue:add_action(pos, "autoconnect_hook_"..name, {node})
	end
end


code:
-- This file registers aliases for the /give /giveme commands.

minetest.register_alias("mesecons:removestone", "mesecons_random:removestone")
minetest.register_alias("mesecons:power_plant", "mesecons_powerplant:power_plant")
minetest.register_alias("mesecons:powerplant", "mesecons_powerplant:power_plant")
minetest.register_alias("mesecons:meselamp", "mesecons_lamp:lamp_off")
minetest.register_alias("mesecons:mesecon", "mesecons:wire_00000000_off")
minetest.register_alias("mesecons:object_detector", "mesecons_detector:object_detector_off")
minetest.register_alias("mesecons:wireless_inverter", "mesecons_wireless:wireless_inverter_on")
minetest.register_alias("mesecons:wireless_receiver", "mesecons_wireless:wireless_receiver_off")
minetest.register_alias("mesecons:wireless_transmitter", "mesecons_wireless:wireless_transmitter_off")
minetest.register_alias("mesecons:switch", "mesecons_switch:mesecon_switch_off")
minetest.register_alias("mesecons:button", "mesecons_button:button_off")
minetest.register_alias("mesecons:piston", "mesecons_pistons:piston_normal_off")
minetest.register_alias("mesecons:blinky_plant", "mesecons_blinkyplant:blinky_plant_off")
minetest.register_alias("mesecons:mesecon_torch", "mesecons_torch:mesecon_torch_on")
minetest.register_alias("mesecons:torch", "mesecons_torch:mesecon_torch_on")
minetest.register_alias("mesecons:hydro_turbine", "mesecons_hydroturbine:hydro_turbine_off")
minetest.register_alias("mesecons:pressure_plate_stone", "mesecons_pressureplates:pressure_plate_stone_off")
minetest.register_alias("mesecons:pressure_plate_wood", "mesecons_pressureplates:pressure_plate_wood_off")
minetest.register_alias("mesecons:mesecon_socket", "mesecons_temperest:mesecon_socket_off")
minetest.register_alias("mesecons:mesecon_inverter", "mesecons_temperest:mesecon_inverter_on")
minetest.register_alias("mesecons:movestone", "mesecons_movestones:movestone")
minetest.register_alias("mesecons:sticky_movestone", "mesecons_movestones:sticky_movestone")
minetest.register_alias("mesecons:noteblock", "mesecons_noteblock:noteblock")
minetest.register_alias("mesecons:microcontroller", "mesecons_microcontroller:microcontroller0000")
minetest.register_alias("mesecons:delayer", "mesecons_delayer:delayer_off_1")
minetest.register_alias("mesecons:solarpanel", "mesecons_solarpanel:solar_panel_off")

--Backwards compatibility
minetest.register_alias("mesecons:mesecon_off", "mesecons:wire_00000000_off")
minetest.register_alias("mesecons_pistons:piston_sticky", "mesecons_pistons:piston_sticky_on")
minetest.register_alias("mesecons_pistons:piston_normal", "mesecons_pistons:piston_normal_on")
minetest.register_alias("mesecons_pistons:piston_up_normal", "mesecons_pistons:piston_up_normal_on")
minetest.register_alias("mesecons_pistons:piston_down_normal", "mesecons_pistons:piston_down_normal_on")
minetest.register_alias("mesecons_pistons:piston_up_sticky", "mesecons_pistons:piston_up_sticky_on")
minetest.register_alias("mesecons_pistons:piston_down_sticky", "mesecons_pistons:piston_down_sticky_on")


code:
name = mesecons_alias
depends = mesecons


code:
The blinky plants toggles between on and off state every three seconds. Can be used to make clocks. Also works after having restarted the game.
It stops blinking in an inactive block, then starts again when the block becomes active.


code:
-- The BLINKY_PLANT

local toggle_timer = function (pos)
	local timer = minetest.get_node_timer(pos)
	if timer:is_started() then
		timer:stop()
	else
		timer:start(mesecon.setting("blinky_plant_interval", 3))
	end
end

local on_timer = function (pos)
	local node = minetest.get_node(pos)
	if(mesecon.flipstate(pos, node) == "on") then
		mesecon.receptor_on(pos)
	else
		mesecon.receptor_off(pos)
	end
	toggle_timer(pos)
end

mesecon.register_node("mesecons_blinkyplant:blinky_plant", {
	description="Blinky Plant",
	drawtype = "plantlike",
	inventory_image = "jeija_blinky_plant_off.png",
	paramtype = "light",
	is_ground_content = false,
	walkable = false,
	sounds = mesecon.node_sound.leaves,
	selection_box = {
		type = "fixed",
		fixed = {-0.3, -0.5, -0.3, 0.3, -0.5+0.7, 0.3},
	},
	on_timer = on_timer,
	on_rightclick = function(pos, _, clicker)
		if minetest.is_protected(pos, clicker and clicker:get_player_name() or "") then
			return
		end

		toggle_timer(pos)
	end,
	on_construct = toggle_timer
},{
	tiles = {"jeija_blinky_plant_off.png"},
	groups = {dig_immediate=3},
	mesecons = {receptor = { state = mesecon.state.off }}
},{
	tiles = {"jeija_blinky_plant_on.png"},
	groups = {dig_immediate=3, not_in_creative_inventory=1},
	mesecons = {receptor = { state = mesecon.state.on }}
})

minetest.register_craft({
	output = "mesecons_blinkyplant:blinky_plant_off 1",
	recipe = {	{"","group:mesecon_conductor_craftable",""},
			{"","group:mesecon_conductor_craftable",""},
			{"group:sapling","group:sapling","group:sapling"}}
})


code:
name = mesecons_blinkyplant
depends = mesecons, mesecons_gamecompat


code: This receptor can be attached to walls. It turns on for 1 second if it's punched.