<commit_before>package notify

import (
	"io/ioutil"
	"strings"
	"sync"
	"time"
)

//notify template string
var templateBody string

//NotifySender is exported
type NotifySender struct {
	sync.RWMutex
	SiteURL   string
	initWatch bool
	endPoints []IEndPoint
	events    map[string]*Event
}

//NewNotifySender is exported
func NewNotifySender(siteurl string, endPoints []EndPoint) *NotifySender {

	sender := &NotifySender{
		SiteURL:   siteurl,
		initWatch: true,
		endPoints: []IEndPoint{},
		events:    make(map[string]*Event),
	}

	if buf, err := ioutil.ReadFile("./notify/template.html"); err == nil {
		templateBody = string(buf)
	}

	factory := &NotifyEndPointFactory{}
	sender.Lock()
	for _, endPoint := range endPoints {
		switch strings.ToUpper(endPoint.Name) {
		case "API":
			apiEndPoint := factory.CreateAPIEndPoint(endPoint)
			sender.endPoints = append(sender.endPoints, apiEndPoint)
		case "SMTP":
			smtpEndPoint := factory.CreateSMTPEndPoint(endPoint)
			sender.endPoints = append(sender.endPoints, smtpEndPoint)
		}
	}
	sender.Unlock()

	go func() {
		time.Sleep(30 * time.Second)
		sender.initWatch = false
	}()
	return sender
}

//AddGroupEnginesWatchEvent is exported
func (sender *NotifySender) AddGroupEnginesWatchEvent(description string, watchGroup *WatchGroup) {

	event := NewEvent(GroupEnginesWatchEvent, description, nil, watchGroup.ContactInfo, sender.SiteURL, sender.endPoints)
	event.data["WatchGroup"] = watchGroup
	sender.Lock()
	sender.events[event.ID] = event
	go sender.dispatchEvents()
	sender.Unlock()
}

//AddGroupMetaContainersEvent is exported
func (sender *NotifySender) AddGroupMetaContainersEvent(description string, err error, groupMeta *GroupMeta) {

	event := NewEvent(GroupMetaContainersEvent, description, err, groupMeta.ContactInfo, sender.SiteURL, sender.endPoints)
	event.data["GroupMeta"] = groupMeta
	sender.Lock()
	sender.events[event.ID] = event
	go sender.dispatchEvents()
	sender.Unlock()
}

//dispatchEvents is exported
//dispatch all events.
func (sender *NotifySender) dispatchEvents() {

	sender.Lock()
	for {
		if len(sender.events) == 0 {
			break
		}
		if !sender.initWatch {
			wgroup := sync.WaitGroup{}
			for _, event := range sender.events {
				wgroup.Add(1)
				go func(e *Event) {
					e.dispatch(templateBody)
					wgroup.Done()
				}(event)
			}
			wgroup.Wait()
		}
		for _, event := range sender.events {
			delete(sender.events, event.ID)
		}
	}
	sender.Unlock()
}
<commit_msg>fix notify.go lock bug.<commit_after>package notify

import (
	"io/ioutil"
	"strings"
	"sync"
	"time"
)

//notify template string
var templateBody string

//NotifySender is exported
type NotifySender struct {
	sync.RWMutex
	SiteURL   string
	initWatch bool
	endPoints []IEndPoint
	events    map[string]*Event
}

//NewNotifySender is exported
func NewNotifySender(siteurl string, endPoints []EndPoint) *NotifySender {

	sender := &NotifySender{
		SiteURL:   siteurl,
		initWatch: true,
		endPoints: []IEndPoint{},
		events:    make(map[string]*Event),
	}

	if buf, err := ioutil.ReadFile("./notify/template.html"); err == nil {
		templateBody = string(buf)
	}

	factory := &NotifyEndPointFactory{}
	sender.Lock()
	for _, endPoint := range endPoints {
		switch strings.ToUpper(endPoint.Name) {
		case "API":
			apiEndPoint := factory.CreateAPIEndPoint(endPoint)
			sender.endPoints = append(sender.endPoints, apiEndPoint)
		case "SMTP":
			smtpEndPoint := factory.CreateSMTPEndPoint(endPoint)
			sender.endPoints = append(sender.endPoints, smtpEndPoint)
		}
	}
	sender.Unlock()

	go func() {
		time.Sleep(30 * time.Second)
		sender.initWatch = false
	}()
	return sender
}

//AddGroupEnginesWatchEvent is exported
func (sender *NotifySender) AddGroupEnginesWatchEvent(description string, watchGroup *WatchGroup) {

	event := NewEvent(GroupEnginesWatchEvent, description, nil, watchGroup.ContactInfo, sender.SiteURL, sender.endPoints)
	event.data["WatchGroup"] = watchGroup
	sender.Lock()
	sender.events[event.ID] = event
	sender.Unlock()
	go sender.dispatchEvents()
}

//AddGroupMetaContainersEvent is exported
func (sender *NotifySender) AddGroupMetaContainersEvent(description string, err error, groupMeta *GroupMeta) {

	event := NewEvent(GroupMetaContainersEvent, description, err, groupMeta.ContactInfo, sender.SiteURL, sender.endPoints)
	event.data["GroupMeta"] = groupMeta
	sender.Lock()
	sender.events[event.ID] = event
	sender.Unlock()
	go sender.dispatchEvents()
}

//dispatchEvents is exported
//dispatch all events.
func (sender *NotifySender) dispatchEvents() {

	sender.Lock()
	for {
		if len(sender.events) == 0 {
			break
		}
		if !sender.initWatch {
			wgroup := sync.WaitGroup{}
			for _, event := range sender.events {
				wgroup.Add(1)
				go func(e *Event) {
					e.dispatch(templateBody)
					wgroup.Done()
				}(event)
			}
			wgroup.Wait()
		}
		for _, event := range sender.events {
			delete(sender.events, event.ID)
		}
	}
	sender.Unlock()
}
<|endoftext|>


<commit_before>package tests

import (
	"bytes"
	"encoding/hex"
	"testing"
	"time"

	"github.com/denkhaus/bitshares/api"
	"github.com/denkhaus/bitshares/objects"

	"github.com/denkhaus/bitshares/util"
	"github.com/stretchr/testify/suite"
)

type operationsAPITest struct {
	suite.Suite
	TestAPI api.BitsharesAPI
}

func (suite *operationsAPITest) SetupTest() {

	api := api.New(wsTestApiUrl, rpcApiUrl)

	if err := api.Connect(); err != nil {
		suite.Fail(err.Error(), "Connect")
	}

	api.OnError(func(err error) {
		suite.Fail(err.Error(), "OnError")
	})

	suite.TestAPI = api
}

func (suite *operationsAPITest) TearDown() {
	if err := suite.TestAPI.Close(); err != nil {
		suite.Fail(err.Error(), "Close")
	}
}

func (suite *operationsAPITest) Test_LimitOrderCancelOperation() {
	time.Sleep(1 * time.Second)
	var buf bytes.Buffer
	enc := util.NewTypeEncoder(&buf)

	tx := objects.NewTransaction()
	tx.RefBlockNum = 555
	tx.RefBlockPrefix = 3333333

	if err := tx.Expiration.UnmarshalJSON([]byte(`"2006-01-02T15:04:05"`)); err != nil {
		suite.Fail(err.Error(), "Unmarshal time")
	}

	op := objects.NewLimitOrderCancelOperation(
		*objects.NewGrapheneID("1.7.69314"),
	)

	op.Order = *objects.NewGrapheneID("1.7.123")
	op.FeePayingAccount = *objects.NewGrapheneID("1.2.456")
	op.Fee = objects.AssetAmount{
		Amount: 1000,
		Asset:  *objects.NewGrapheneID("1.3.789"),
	}

	tx.Operations = append(tx.Operations, op)
	if err := enc.Encode(tx); err != nil {
		suite.Fail(err.Error(), "Encode")
	}

	res := hex.EncodeToString(buf.Bytes())
	suite.Equal("2b02d5dc3200e540b9430102e8030000000000009506c8037b000000", res)
}

func TestOperations(t *testing.T) {
	testSuite := new(operationsAPITest)
	suite.Run(t, testSuite)
	testSuite.TearDown()
}
<commit_msg>change op test<commit_after>package tests

import (
	"bytes"
	"encoding/hex"
	"testing"
	"time"

	"github.com/denkhaus/bitshares/api"
	"github.com/denkhaus/bitshares/objects"

	"github.com/denkhaus/bitshares/util"
	"github.com/stretchr/testify/suite"
)

type operationsAPITest struct {
	suite.Suite
	TestAPI api.BitsharesAPI
}

func (suite *operationsAPITest) SetupTest() {

	api := api.New(wsTestApiUrl, rpcApiUrl)

	if err := api.Connect(); err != nil {
		suite.Fail(err.Error(), "Connect")
	}

	api.OnError(func(err error) {
		suite.Fail(err.Error(), "OnError")
	})

	suite.TestAPI = api
}

func (suite *operationsAPITest) TearDown() {
	if err := suite.TestAPI.Close(); err != nil {
		suite.Fail(err.Error(), "Close")
	}
}

func (suite *operationsAPITest) Test_LimitOrderCancelOperation() {
	time.Sleep(1 * time.Second)

	tx := objects.NewTransaction()
	tx.RefBlockNum = 555
	tx.RefBlockPrefix = 3333333

	if err := tx.Expiration.UnmarshalJSON([]byte(`"2006-01-02T15:04:05"`)); err != nil {
		suite.Fail(err.Error(), "Unmarshal time")
	}

	op := objects.LimitOrderCancelOperation{
		Extensions:       objects.Extensions{},
		Order:            *objects.NewGrapheneID("1.7.123"),
		FeePayingAccount: *objects.NewGrapheneID("1.2.456"),
		Fee: objects.AssetAmount{
			Amount: 1000,
			Asset:  *objects.NewGrapheneID("1.3.789"),
		},
	}

	tx.Operations = append(tx.Operations, objects.Operation(&op))

	var buf bytes.Buffer
	enc := util.NewTypeEncoder(&buf)
	if err := enc.Encode(tx); err != nil {
		suite.Fail(err.Error(), "Encode")
	}

	res := hex.EncodeToString(buf.Bytes())
	suite.Equal("2b02d5dc3200e540b9430102e8030000000000009506c8037b000000", res)
}

func TestOperations(t *testing.T) {
	testSuite := new(operationsAPITest)
	suite.Run(t, testSuite)
	testSuite.TearDown()
}
<|endoftext|>


<commit_before>package ethrpc

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"math/big"
	"net/http"
)

type ethError struct {
	Code    int    `json:"code"`
	Message string `json:"message"`
}

func (err ethError) Error() string {
	return fmt.Sprintf("Error %d (%s)", err.Code, err.Message)
}

type ethResponse struct {
	ID      int             `json:"id"`
	JSONRPC string          `json:"jsonrpc"`
	Result  json.RawMessage `json:"result"`
	Error   *ethError       `json:"error"`
}

type ethRequest struct {
	ID      int           `json:"id"`
	JSONRPC string        `json:"jsonrpc"`
	Method  string        `json:"method"`
	Params  []interface{} `json:"params"`
}

// EthRPC - Ethereum rpc client
type EthRPC struct {
	url   string
	Debug bool
}

// NewEthRPC create new rpc client with given url
func NewEthRPC(url string) *EthRPC {
	return &EthRPC{url: url}
}

func (rpc *EthRPC) call(method string, target interface{}, params ...interface{}) error {
	request := ethRequest{
		ID:      1,
		JSONRPC: "2.0",
		Method:  method,
		Params:  params,
	}

	body, err := json.Marshal(request)
	if err != nil {
		return err
	}

	response, err := http.Post(rpc.url, "application/json", bytes.NewBuffer(body))
	if response != nil {
		defer response.Body.Close()
	}
	if err != nil {
		return err
	}

	data, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return err
	}

	if rpc.Debug {
		log.Printf("%s\nRequest: %s\nResponse: %s\n", method, body, data)
	}

	resp := new(ethResponse)
	if err := json.Unmarshal(data, resp); err != nil {
		return err
	}

	if resp.Error != nil {
		return resp.Error
	}

	if target == nil {
		return nil
	}

	if err := json.Unmarshal(resp.Result, target); err != nil {
		return err
	}

	return nil
}

// Web3ClientVersion returns the current client version.
func (rpc *EthRPC) Web3ClientVersion() (string, error) {
	var clientVersion string

	err := rpc.call("web3_clientVersion", &clientVersion)
	return clientVersion, err
}

// Web3Sha3 returns Keccak-256 (not the standardized SHA3-256) of the given data.
func (rpc *EthRPC) Web3Sha3(data []byte) (string, error) {
	var hash string

	err := rpc.call("web3_sha3", &hash, fmt.Sprintf("0x%x", data))
	return hash, err
}

// NetVersion returns the current network protocol version.
func (rpc *EthRPC) NetVersion() (string, error) {
	var version string

	err := rpc.call("net_version", &version)
	return version, err
}

// NetListening returns true if client is actively listening for network connections.
func (rpc *EthRPC) NetListening() (bool, error) {
	var listening bool

	err := rpc.call("net_listening", &listening)
	return listening, err
}

// NetPeerCount returns number of peers currently connected to the client.
func (rpc *EthRPC) NetPeerCount() (int, error) {
	var response string
	if err := rpc.call("net_peerCount", &response); err != nil {
		return 0, err
	}

	return ParseInt(response)
}

// EthProtocolVersion returns the current ethereum protocol version.
func (rpc *EthRPC) EthProtocolVersion() (string, error) {
	var protocolVersion string

	err := rpc.call("eth_protocolVersion", &protocolVersion)
	return protocolVersion, err
}

// EthSyncing returns an object with data about the sync status or false.
// func (rpc *EthRPC) EthSyncing() (bool, error) {

// 	err := rpc.call("eth_syncing",)
// 	return syncing, err
// }

// EthCoinbase returns the client coinbase address
func (rpc *EthRPC) EthCoinbase() (string, error) {
	var address string

	err := rpc.call("eth_coinbase", &address)
	return address, err
}

// EthMining returns true if client is actively mining new blocks.
func (rpc *EthRPC) EthMining() (bool, error) {
	var mining bool

	err := rpc.call("eth_mining", &mining)
	return mining, err
}

// EthHashrate returns the number of hashes per second that the node is mining with.
func (rpc *EthRPC) EthHashrate() (int, error) {
	var response string

	if err := rpc.call("eth_hashrate", &response); err != nil {
		return 0, err
	}

	return ParseInt(response)
}

// EthGasPrice returns the current price per gas in wei.
func (rpc *EthRPC) EthGasPrice() (big.Int, error) {
	var response string
	if err := rpc.call("eth_gasPrice", &response); err != nil {
		return big.Int{}, err
	}

	return ParseBigInt(response)
}

// EthAccounts returns a list of addresses owned by client.
func (rpc *EthRPC) EthAccounts() ([]string, error) {
	accounts := []string{}

	err := rpc.call("eth_accounts", &accounts)
	return accounts, err
}

// EthBlockNumber returns the number of most recent block.
func (rpc *EthRPC) EthBlockNumber() (int, error) {
	var response string
	if err := rpc.call("eth_blockNumber", &response); err != nil {
		return 0, err
	}

	return ParseInt(response)
}

// EthGetBalance returns the balance of the account of given address in wei.
func (rpc *EthRPC) EthGetBalance(address, block string) (big.Int, error) {
	var response string
	if err := rpc.call("eth_getBalance", &response, address, block); err != nil {
		return big.Int{}, err
	}

	return ParseBigInt(response)
}

// EthGetTransactionCount returns the number of transactions sent from an address.
func (rpc *EthRPC) EthGetTransactionCount(address, block string) (int, error) {
	var response string

	if err := rpc.call("eth_getTransactionCount", &response, address, block); err != nil {
		return 0, err
	}

	return ParseInt(response)
}

// EthGetBlockTransactionCountByHash returns the number of transactions in a block from a block matching the given block hash.
func (rpc *EthRPC) EthGetBlockTransactionCountByHash(hash string) (int, error) {
	var response string

	if err := rpc.call("eth_getBlockTransactionCountByHash", &response, hash); err != nil {
		return 0, err
	}

	return ParseInt(response)
}

// EthGetBlockTransactionCountByNumber returns the number of transactions in a block from a block matching the given block
func (rpc *EthRPC) EthGetBlockTransactionCountByNumber(number int) (int, error) {
	var response string

	if err := rpc.call("eth_getBlockTransactionCountByNumber", &response, fmt.Sprintf("0x%x", number)); err != nil {
		return 0, err
	}

	return ParseInt(response)
}

// EthGetUncleCountByBlockHash returns the number of uncles in a block from a block matching the given block hash.
func (rpc *EthRPC) EthGetUncleCountByBlockHash(hash string) (int, error) {
	var response string

	if err := rpc.call("eth_getUncleCountByBlockHash", &response, hash); err != nil {
		return 0, err
	}

	return ParseInt(response)
}

// EthGetUncleCountByBlockNumber returns the number of uncles in a block from a block matching the given block number.
func (rpc *EthRPC) EthGetUncleCountByBlockNumber(number int) (int, error) {
	var response string

	if err := rpc.call("eth_getUncleCountByBlockNumber", &response, fmt.Sprintf("0x%x", number)); err != nil {
		return 0, err
	}

	return ParseInt(response)
}

// EthGetCode returns code at a given address.
func (rpc *EthRPC) EthGetCode(address, block string) (string, error) {
	var code string

	err := rpc.call("eth_getCode", &code, address, block)
	return code, err
}

// EthSign signs data with a given address.
func (rpc *EthRPC) EthSign(address, data string) (string, error) {
	var signature string

	err := rpc.call("eth_sign", &signature, address, data)
	return signature, err
}

// EthSendTransaction creates new message call transaction or a contract creation, if the data field contains code.
func (rpc *EthRPC) EthSendTransaction(transaction *T) (string, error) {
	var hash string

	err := rpc.call("eth_sendTransaction", &hash, transaction)
	return hash, err
}

// EthSendRawTransaction creates new message call transaction or a contract creation for signed transactions.
func (rpc *EthRPC) EthSendRawTransaction(data string) (string, error) {
	var hash string

	err := rpc.call("eth_sendRawTransaction", &hash, data)
	return hash, err
}

// EthCall executes a new message call immediately without creating a transaction on the block chain.
func (rpc *EthRPC) EthCall(transaction *T, tag string) (string, error) {
	var data string

	err := rpc.call("eth_call", &data, transaction, tag)
	return data, err
}

// EthEstimateGas makes a call or transaction, which won't be added to the blockchain and returns the used gas, which can be used for estimating the used gas.
func (rpc *EthRPC) EthEstimateGas(transaction *T) (int, error) {
	var response string

	err := rpc.call("eth_estimateGas", &response, transaction)
	if err != nil {
		return 0, nil
	}

	return ParseInt(response)
}

// EthGetCompilers returns a list of available compilers in the client.
func (rpc *EthRPC) EthGetCompilers() ([]string, error) {
	compilers := []string{}

	err := rpc.call("eth_getCompilers", &compilers)
	return compilers, err
}
<commit_msg>Fix typo in EthEstimateGas.<commit_after>package ethrpc

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"math/big"
	"net/http"
)

type ethError struct {
	Code    int    `json:"code"`
	Message string `json:"message"`
}

func (err ethError) Error() string {
	return fmt.Sprintf("Error %d (%s)", err.Code, err.Message)
}

type ethResponse struct {
	ID      int             `json:"id"`
	JSONRPC string          `json:"jsonrpc"`
	Result  json.RawMessage `json:"result"`
	Error   *ethError       `json:"error"`
}

type ethRequest struct {
	ID      int           `json:"id"`
	JSONRPC string        `json:"jsonrpc"`
	Method  string        `json:"method"`
	Params  []interface{} `json:"params"`
}

// EthRPC - Ethereum rpc client
type EthRPC struct {
	url   string
	Debug bool
}

// NewEthRPC create new rpc client with given url
func NewEthRPC(url string) *EthRPC {
	return &EthRPC{url: url}
}

func (rpc *EthRPC) call(method string, target interface{}, params ...interface{}) error {
	request := ethRequest{
		ID:      1,
		JSONRPC: "2.0",
		Method:  method,
		Params:  params,
	}

	body, err := json.Marshal(request)
	if err != nil {
		return err
	}

	response, err := http.Post(rpc.url, "application/json", bytes.NewBuffer(body))
	if response != nil {
		defer response.Body.Close()
	}
	if err != nil {
		return err
	}

	data, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return err
	}

	if rpc.Debug {
		log.Printf("%s\nRequest: %s\nResponse: %s\n", method, body, data)
	}

	resp := new(ethResponse)
	if err := json.Unmarshal(data, resp); err != nil {
		return err
	}

	if resp.Error != nil {
		return resp.Error
	}

	if target == nil {
		return nil
	}

	if err := json.Unmarshal(resp.Result, target); err != nil {
		return err
	}

	return nil
}

// Web3ClientVersion returns the current client version.
func (rpc *EthRPC) Web3ClientVersion() (string, error) {
	var clientVersion string

	err := rpc.call("web3_clientVersion", &clientVersion)
	return clientVersion, err
}

// Web3Sha3 returns Keccak-256 (not the standardized SHA3-256) of the given data.
func (rpc *EthRPC) Web3Sha3(data []byte) (string, error) {
	var hash string

	err := rpc.call("web3_sha3", &hash, fmt.Sprintf("0x%x", data))
	return hash, err
}

// NetVersion returns the current network protocol version.
func (rpc *EthRPC) NetVersion() (string, error) {
	var version string

	err := rpc.call("net_version", &version)
	return version, err
}

// NetListening returns true if client is actively listening for network connections.
func (rpc *EthRPC) NetListening() (bool, error) {
	var listening bool

	err := rpc.call("net_listening", &listening)
	return listening, err
}

// NetPeerCount returns number of peers currently connected to the client.
func (rpc *EthRPC) NetPeerCount() (int, error) {
	var response string
	if err := rpc.call("net_peerCount", &response); err != nil {
		return 0, err
	}

	return ParseInt(response)
}

// EthProtocolVersion returns the current ethereum protocol version.
func (rpc *EthRPC) EthProtocolVersion() (string, error) {
	var protocolVersion string

	err := rpc.call("eth_protocolVersion", &protocolVersion)
	return protocolVersion, err
}

// EthSyncing returns an object with data about the sync status or false.
// func (rpc *EthRPC) EthSyncing() (bool, error) {

// 	err := rpc.call("eth_syncing",)
// 	return syncing, err
// }

// EthCoinbase returns the client coinbase address
func (rpc *EthRPC) EthCoinbase() (string, error) {
	var address string

	err := rpc.call("eth_coinbase", &address)
	return address, err
}

// EthMining returns true if client is actively mining new blocks.
func (rpc *EthRPC) EthMining() (bool, error) {
	var mining bool

	err := rpc.call("eth_mining", &mining)
	return mining, err
}

// EthHashrate returns the number of hashes per second that the node is mining with.
func (rpc *EthRPC) EthHashrate() (int, error) {
	var response string

	if err := rpc.call("eth_hashrate", &response); err != nil {
		return 0, err
	}

	return ParseInt(response)
}

// EthGasPrice returns the current price per gas in wei.
func (rpc *EthRPC) EthGasPrice() (big.Int, error) {
	var response string
	if err := rpc.call("eth_gasPrice", &response); err != nil {
		return big.Int{}, err
	}

	return ParseBigInt(response)
}

// EthAccounts returns a list of addresses owned by client.
func (rpc *EthRPC) EthAccounts() ([]string, error) {
	accounts := []string{}

	err := rpc.call("eth_accounts", &accounts)
	return accounts, err
}

// EthBlockNumber returns the number of most recent block.
func (rpc *EthRPC) EthBlockNumber() (int, error) {
	var response string
	if err := rpc.call("eth_blockNumber", &response); err != nil {
		return 0, err
	}

	return ParseInt(response)
}

// EthGetBalance returns the balance of the account of given address in wei.
func (rpc *EthRPC) EthGetBalance(address, block string) (big.Int, error) {
	var response string
	if err := rpc.call("eth_getBalance", &response, address, block); err != nil {
		return big.Int{}, err
	}

	return ParseBigInt(response)
}

// EthGetTransactionCount returns the number of transactions sent from an address.
func (rpc *EthRPC) EthGetTransactionCount(address, block string) (int, error) {
	var response string

	if err := rpc.call("eth_getTransactionCount", &response, address, block); err != nil {
		return 0, err
	}

	return ParseInt(response)
}

// EthGetBlockTransactionCountByHash returns the number of transactions in a block from a block matching the given block hash.
func (rpc *EthRPC) EthGetBlockTransactionCountByHash(hash string) (int, error) {
	var response string

	if err := rpc.call("eth_getBlockTransactionCountByHash", &response, hash); err != nil {
		return 0, err
	}

	return ParseInt(response)
}

// EthGetBlockTransactionCountByNumber returns the number of transactions in a block from a block matching the given block
func (rpc *EthRPC) EthGetBlockTransactionCountByNumber(number int) (int, error) {
	var response string

	if err := rpc.call("eth_getBlockTransactionCountByNumber", &response, fmt.Sprintf("0x%x", number)); err != nil {
		return 0, err
	}

	return ParseInt(response)
}

// EthGetUncleCountByBlockHash returns the number of uncles in a block from a block matching the given block hash.
func (rpc *EthRPC) EthGetUncleCountByBlockHash(hash string) (int, error) {
	var response string

	if err := rpc.call("eth_getUncleCountByBlockHash", &response, hash); err != nil {
		return 0, err
	}

	return ParseInt(response)
}

// EthGetUncleCountByBlockNumber returns the number of uncles in a block from a block matching the given block number.
func (rpc *EthRPC) EthGetUncleCountByBlockNumber(number int) (int, error) {
	var response string

	if err := rpc.call("eth_getUncleCountByBlockNumber", &response, fmt.Sprintf("0x%x", number)); err != nil {
		return 0, err
	}

	return ParseInt(response)
}

// EthGetCode returns code at a given address.
func (rpc *EthRPC) EthGetCode(address, block string) (string, error) {
	var code string

	err := rpc.call("eth_getCode", &code, address, block)
	return code, err
}

// EthSign signs data with a given address.
func (rpc *EthRPC) EthSign(address, data string) (string, error) {
	var signature string

	err := rpc.call("eth_sign", &signature, address, data)
	return signature, err
}

// EthSendTransaction creates new message call transaction or a contract creation, if the data field contains code.
func (rpc *EthRPC) EthSendTransaction(transaction *T) (string, error) {
	var hash string

	err := rpc.call("eth_sendTransaction", &hash, transaction)
	return hash, err
}

// EthSendRawTransaction creates new message call transaction or a contract creation for signed transactions.
func (rpc *EthRPC) EthSendRawTransaction(data string) (string, error) {
	var hash string

	err := rpc.call("eth_sendRawTransaction", &hash, data)
	return hash, err
}

// EthCall executes a new message call immediately without creating a transaction on the block chain.
func (rpc *EthRPC) EthCall(transaction *T, tag string) (string, error) {
	var data string

	err := rpc.call("eth_call", &data, transaction, tag)
	return data, err
}

// EthEstimateGas makes a call or transaction, which won't be added to the blockchain and returns the used gas, which can be used for estimating the used gas.
func (rpc *EthRPC) EthEstimateGas(transaction *T) (int, error) {
	var response string

	err := rpc.call("eth_estimateGas", &response, transaction)
	if err != nil {
		return 0, err
	}

	return ParseInt(response)
}

// EthGetCompilers returns a list of available compilers in the client.
func (rpc *EthRPC) EthGetCompilers() ([]string, error) {
	compilers := []string{}

	err := rpc.call("eth_getCompilers", &compilers)
	return compilers, err
}
<|endoftext|>


<commit_before>package main

import "container/heap"

type event interface {
	Rank() int
	Action(*game)
	Renew(*game, int)
}

type iEvent struct {
	Event event
	Index int
}

type eventQueue []iEvent

func (evq eventQueue) Len() int {
	return len(evq)
}

func (evq eventQueue) Less(i, j int) bool {
	return evq[i].Event.Rank() < evq[j].Event.Rank() ||
		evq[i].Event.Rank() == evq[j].Event.Rank() && evq[i].Index < evq[j].Index
}

func (evq eventQueue) Swap(i, j int) {
	evq[i], evq[j] = evq[j], evq[i]
}

func (evq *eventQueue) Push(x interface{}) {
	no := x.(iEvent)
	*evq = append(*evq, no)
}

func (evq *eventQueue) Pop() interface{} {
	old := *evq
	n := len(old)
	no := old[n-1]
	*evq = old[0 : n-1]
	return no
}

type simpleAction int

const (
	PlayerTurn simpleAction = iota
	HealPlayer
	MPRegen
	Teleportation
	BerserkEnd
	SlowEnd
	ExhaustionEnd
	HasteEnd
	EvasionEnd
	LignificationEnd
	ConfusionEnd
	NauseaEnd
	DisabledShieldEnd
	CorrosionEnd
	DigEnd
	SwapEnd
)

func (g *game) PushEvent(ev event) {
	iev := iEvent{Event: ev, Index: g.EventIndex}
	g.EventIndex++
	heap.Push(g.Events, iev)
}

func (g *game) PushAgainEvent(ev event) {
	iev := iEvent{Event: ev, Index: 0}
	heap.Push(g.Events, iev)
}

func (g *game) PopIEvent() iEvent {
	iev := heap.Pop(g.Events).(iEvent)
	return iev
}

type simpleEvent struct {
	ERank   int
	EAction simpleAction
}

func (sev *simpleEvent) Rank() int {
	return sev.ERank
}

func (sev *simpleEvent) Renew(g *game, delay int) {
	sev.ERank += delay
	if delay == 0 {
		g.PushAgainEvent(sev)
	} else {
		g.PushEvent(sev)
	}
}

func (sev *simpleEvent) Action(g *game) {
	switch sev.EAction {
	case PlayerTurn:
		g.ComputeNoise()
		g.LogNextTick = g.LogIndex
		g.AutoNext = g.AutoPlayer(sev)
		if g.AutoNext {
			g.Stats.Turns++
			return
		}
		g.Quit = g.ui.HandlePlayerTurn(g, sev)
		if g.Quit {
			return
		}
		g.TurnStats()
	case HealPlayer:
		g.HealPlayer(sev)
	case MPRegen:
		g.MPRegen(sev)
	case Teleportation:
		if !g.Player.HasStatus(StatusLignification) {
			g.Teleportation(sev)
		} else {
			g.Print("Lignification prevented teleportation.")
		}
		g.Player.Statuses[StatusTele] = 0
	case BerserkEnd:
		g.Player.Statuses[StatusBerserk] = 0
		g.Player.Statuses[StatusSlow] = 1
		g.Player.Statuses[StatusExhausted] = 1
		g.Player.HP -= int(10 * g.Player.HP / Max(g.Player.HPMax(), g.Player.HP))
		g.PrintStyled("You are no longer berserk.", logStatusEnd)
		g.PushEvent(&simpleEvent{ERank: sev.Rank() + 90 + RandInt(40), EAction: SlowEnd})
		g.PushEvent(&simpleEvent{ERank: sev.Rank() + 270 + RandInt(60), EAction: ExhaustionEnd})
		g.ui.StatusEndAnimation(g)
	case SlowEnd:
		g.PrintStyled("You feel no longer slow.", logStatusEnd)
		g.Player.Statuses[StatusSlow] = 0
		g.ui.StatusEndAnimation(g)
	case ExhaustionEnd:
		g.PrintStyled("You feel no longer exhausted.", logStatusEnd)
		g.Player.Statuses[StatusExhausted] = 0
		g.ui.StatusEndAnimation(g)
	case HasteEnd:
		g.Player.Statuses[StatusSwift]--
		if g.Player.Statuses[StatusSwift] == 0 {
			g.PrintStyled("You feel no longer speedy.", logStatusEnd)
			g.ui.StatusEndAnimation(g)
		}
	case EvasionEnd:
		g.Player.Statuses[StatusAgile]--
		if g.Player.Statuses[StatusAgile] == 0 {
			g.PrintStyled("You feel no longer agile.", logStatusEnd)
			g.ui.StatusEndAnimation(g)
		}
	case LignificationEnd:
		g.Player.Statuses[StatusLignification]--
		g.Player.HP -= int(10 * g.Player.HP / Max(g.Player.HPMax(), g.Player.HP))
		if g.Player.Statuses[StatusLignification] == 0 {
			g.PrintStyled("You feel no longer attached to the ground.", logStatusEnd)
			g.ui.StatusEndAnimation(g)
		}
	case ConfusionEnd:
		g.PrintStyled("You feel no longer confused.", logStatusEnd)
		g.Player.Statuses[StatusConfusion] = 0
		g.ui.StatusEndAnimation(g)
	case NauseaEnd:
		g.PrintStyled("You feel no longer sick.", logStatusEnd)
		g.Player.Statuses[StatusNausea] = 0
		g.ui.StatusEndAnimation(g)
	case DisabledShieldEnd:
		g.PrintStyled("You manage to free your shield from the projectile.", logStatusEnd)
		g.Player.Statuses[StatusDisabledShield] = 0
		g.ui.StatusEndAnimation(g)
	case CorrosionEnd:
		g.Player.Statuses[StatusCorrosion]--
		if g.Player.Statuses[StatusCorrosion] == 0 {
			g.PrintStyled("Your equipment is now free from acid.", logStatusEnd)
			g.ui.StatusEndAnimation(g)
		}
	case DigEnd:
		g.Player.Statuses[StatusDig] = 0
		if g.Player.Statuses[StatusDig] == 0 {
			g.PrintStyled("You feel no longer like an earth dragon.", logStatusEnd)
			g.ui.StatusEndAnimation(g)
		}
	case SwapEnd:
		g.Player.Statuses[StatusSwap] = 0
		if g.Player.Statuses[StatusSwap] == 0 {
			g.PrintStyled("You feel no longer dancing.", logStatusEnd)
			g.ui.StatusEndAnimation(g)
		}
	}
}

type monsterAction int

const (
	MonsterTurn monsterAction = iota
	HealMonster
	MonsConfusionEnd
	MonsExhaustionEnd
)

type monsterEvent struct {
	ERank   int
	NMons   int
	EAction monsterAction
}

func (mev *monsterEvent) Rank() int {
	return mev.ERank
}

func (mev *monsterEvent) Action(g *game) {
	switch mev.EAction {
	case MonsterTurn:
		mons := g.Monsters[mev.NMons]
		if mons.Exists() {
			mons.HandleTurn(g, mev)
		}
	case HealMonster:
		mons := g.Monsters[mev.NMons]
		if mons.Exists() {
			mons.Heal(g, mev)
		}
	case MonsConfusionEnd:
		mons := g.Monsters[mev.NMons]
		if mons.Exists() {
			mons.Statuses[MonsConfused] = 0
			g.Printf("The %s is no longer confused.", mons.Kind)
		}
	case MonsExhaustionEnd:
		mons := g.Monsters[mev.NMons]
		if mons.Exists() {
			mons.Statuses[MonsExhausted] = 0
		}
	}
}

func (mev *monsterEvent) Renew(g *game, delay int) {
	mev.ERank += delay
	g.PushEvent(mev)
}

type cloudAction int

const (
	CloudEnd cloudAction = iota
	ObstructionEnd
	FireProgression
)

type cloudEvent struct {
	ERank   int
	Pos     position
	EAction cloudAction
}

func (cev *cloudEvent) Rank() int {
	return cev.ERank
}

func (cev *cloudEvent) Action(g *game) {
	switch cev.EAction {
	case CloudEnd:
		delete(g.Clouds, cev.Pos)
		g.ComputeLOS()
	case ObstructionEnd:
		if !g.Player.LOS[cev.Pos] && g.Dungeon.Cell(cev.Pos).T == WallCell {
			g.UnknownDig[cev.Pos] = true
		} else {
			delete(g.TemporalWalls, cev.Pos)
		}
		if g.Dungeon.Cell(cev.Pos).T == FreeCell {
			break
		}
		g.Dungeon.SetCell(cev.Pos, FreeCell)
		g.MakeNoise(15, cev.Pos)
		g.Fog(cev.Pos, 1, &simpleEvent{ERank: cev.Rank()})
		g.ComputeLOS()
	case FireProgression:
		if _, ok := g.Clouds[cev.Pos]; !ok {
			break
		}
		g.BurnCreature(cev.Pos, cev)
		if RandInt(10) == 0 {
			delete(g.Clouds, cev.Pos)
			g.Fog(cev.Pos, 1, &simpleEvent{ERank: cev.Rank()})
			g.ComputeLOS()
			break
		}
		for _, pos := range g.Dungeon.FreeNeighbors(cev.Pos) {
			if RandInt(3) > 0 {
				continue
			}
			g.Burn(pos, cev)
		}
		cev.Renew(g, 10)
	}
}

func (g *game) BurnCreature(pos position, ev event) {
	mons := g.MonsterAt(pos)
	if mons.Exists() {
		mons.HP -= 1 + RandInt(10)
		if mons.HP <= 0 {
			g.PrintfStyled("%s is killed by the fire.", logPlayerHit, mons.Kind.Definite(true))
			g.HandleKill(mons, ev)
		} else {
			mons.MakeAwareIfHurt(g)
		}
	}
	if pos == g.Player.Pos {
		damage := 1 + RandInt(10)
		if damage > g.Player.HP {
			damage = 1 + RandInt(10)
		}
		g.Player.HP -= damage
		g.PrintfStyled("The fire burns you (%d dmg).", logMonsterHit, damage)
		if g.Player.HP+damage < 10 {
			g.Stats.TimesLucky++
		}
		g.StopAuto()
	}
}

type burn int

const (
	NoUnknownBurn burn = iota
	FoliageBurn
	DoorBurn
)

func (g *game) Burn(pos position, ev event) {
	if _, ok := g.Clouds[pos]; ok {
		return
	}
	_, okFungus := g.Fungus[pos]
	_, okDoor := g.Doors[pos]
	if !okFungus && !okDoor {
		return
	}
	g.Stats.Burns++
	brn := FoliageBurn
	delete(g.Fungus, pos)
	if _, ok := g.Doors[pos]; ok {
		delete(g.Doors, pos)
		brn = DoorBurn
		g.Print("The door vanishes in flames.")
	}
	g.Clouds[pos] = CloudFire
	if !g.Player.LOS[pos] {
		g.UnknownBurn[pos] = brn
	}
	g.PushEvent(&cloudEvent{ERank: ev.Rank() + 10, EAction: FireProgression, Pos: pos})
	g.BurnCreature(pos, ev)
}

func (cev *cloudEvent) Renew(g *game, delay int) {
	cev.ERank += delay
	g.PushEvent(cev)
}
<commit_msg>a little less random slow timeout<commit_after>package main

import "container/heap"

type event interface {
	Rank() int
	Action(*game)
	Renew(*game, int)
}

type iEvent struct {
	Event event
	Index int
}

type eventQueue []iEvent

func (evq eventQueue) Len() int {
	return len(evq)
}

func (evq eventQueue) Less(i, j int) bool {
	return evq[i].Event.Rank() < evq[j].Event.Rank() ||
		evq[i].Event.Rank() == evq[j].Event.Rank() && evq[i].Index < evq[j].Index
}

func (evq eventQueue) Swap(i, j int) {
	evq[i], evq[j] = evq[j], evq[i]
}

func (evq *eventQueue) Push(x interface{}) {
	no := x.(iEvent)
	*evq = append(*evq, no)
}

func (evq *eventQueue) Pop() interface{} {
	old := *evq
	n := len(old)
	no := old[n-1]
	*evq = old[0 : n-1]
	return no
}

type simpleAction int

const (
	PlayerTurn simpleAction = iota
	HealPlayer
	MPRegen
	Teleportation
	BerserkEnd
	SlowEnd
	ExhaustionEnd
	HasteEnd
	EvasionEnd
	LignificationEnd
	ConfusionEnd
	NauseaEnd
	DisabledShieldEnd
	CorrosionEnd
	DigEnd
	SwapEnd
)

func (g *game) PushEvent(ev event) {
	iev := iEvent{Event: ev, Index: g.EventIndex}
	g.EventIndex++
	heap.Push(g.Events, iev)
}

func (g *game) PushAgainEvent(ev event) {
	iev := iEvent{Event: ev, Index: 0}
	heap.Push(g.Events, iev)
}

func (g *game) PopIEvent() iEvent {
	iev := heap.Pop(g.Events).(iEvent)
	return iev
}

type simpleEvent struct {
	ERank   int
	EAction simpleAction
}

func (sev *simpleEvent) Rank() int {
	return sev.ERank
}

func (sev *simpleEvent) Renew(g *game, delay int) {
	sev.ERank += delay
	if delay == 0 {
		g.PushAgainEvent(sev)
	} else {
		g.PushEvent(sev)
	}
}

func (sev *simpleEvent) Action(g *game) {
	switch sev.EAction {
	case PlayerTurn:
		g.ComputeNoise()
		g.LogNextTick = g.LogIndex
		g.AutoNext = g.AutoPlayer(sev)
		if g.AutoNext {
			g.Stats.Turns++
			return
		}
		g.Quit = g.ui.HandlePlayerTurn(g, sev)
		if g.Quit {
			return
		}
		g.TurnStats()
	case HealPlayer:
		g.HealPlayer(sev)
	case MPRegen:
		g.MPRegen(sev)
	case Teleportation:
		if !g.Player.HasStatus(StatusLignification) {
			g.Teleportation(sev)
		} else {
			g.Print("Lignification prevented teleportation.")
		}
		g.Player.Statuses[StatusTele] = 0
	case BerserkEnd:
		g.Player.Statuses[StatusBerserk] = 0
		g.Player.Statuses[StatusSlow] = 1
		g.Player.Statuses[StatusExhausted] = 1
		g.Player.HP -= int(10 * g.Player.HP / Max(g.Player.HPMax(), g.Player.HP))
		g.PrintStyled("You are no longer berserk.", logStatusEnd)
		g.PushEvent(&simpleEvent{ERank: sev.Rank() + 90 + RandInt(30), EAction: SlowEnd})
		g.PushEvent(&simpleEvent{ERank: sev.Rank() + 270 + RandInt(60), EAction: ExhaustionEnd})
		g.ui.StatusEndAnimation(g)
	case SlowEnd:
		g.PrintStyled("You feel no longer slow.", logStatusEnd)
		g.Player.Statuses[StatusSlow] = 0
		g.ui.StatusEndAnimation(g)
	case ExhaustionEnd:
		g.PrintStyled("You feel no longer exhausted.", logStatusEnd)
		g.Player.Statuses[StatusExhausted] = 0
		g.ui.StatusEndAnimation(g)
	case HasteEnd:
		g.Player.Statuses[StatusSwift]--
		if g.Player.Statuses[StatusSwift] == 0 {
			g.PrintStyled("You feel no longer speedy.", logStatusEnd)
			g.ui.StatusEndAnimation(g)
		}
	case EvasionEnd:
		g.Player.Statuses[StatusAgile]--
		if g.Player.Statuses[StatusAgile] == 0 {
			g.PrintStyled("You feel no longer agile.", logStatusEnd)
			g.ui.StatusEndAnimation(g)
		}
	case LignificationEnd:
		g.Player.Statuses[StatusLignification]--
		g.Player.HP -= int(10 * g.Player.HP / Max(g.Player.HPMax(), g.Player.HP))
		if g.Player.Statuses[StatusLignification] == 0 {
			g.PrintStyled("You feel no longer attached to the ground.", logStatusEnd)
			g.ui.StatusEndAnimation(g)
		}
	case ConfusionEnd:
		g.PrintStyled("You feel no longer confused.", logStatusEnd)
		g.Player.Statuses[StatusConfusion] = 0
		g.ui.StatusEndAnimation(g)
	case NauseaEnd:
		g.PrintStyled("You feel no longer sick.", logStatusEnd)
		g.Player.Statuses[StatusNausea] = 0
		g.ui.StatusEndAnimation(g)
	case DisabledShieldEnd:
		g.PrintStyled("You manage to free your shield from the projectile.", logStatusEnd)
		g.Player.Statuses[StatusDisabledShield] = 0
		g.ui.StatusEndAnimation(g)
	case CorrosionEnd:
		g.Player.Statuses[StatusCorrosion]--
		if g.Player.Statuses[StatusCorrosion] == 0 {
			g.PrintStyled("Your equipment is now free from acid.", logStatusEnd)
			g.ui.StatusEndAnimation(g)
		}
	case DigEnd:
		g.Player.Statuses[StatusDig] = 0
		if g.Player.Statuses[StatusDig] == 0 {
			g.PrintStyled("You feel no longer like an earth dragon.", logStatusEnd)
			g.ui.StatusEndAnimation(g)
		}
	case SwapEnd:
		g.Player.Statuses[StatusSwap] = 0
		if g.Player.Statuses[StatusSwap] == 0 {
			g.PrintStyled("You feel no longer dancing.", logStatusEnd)
			g.ui.StatusEndAnimation(g)
		}
	}
}

type monsterAction int

const (
	MonsterTurn monsterAction = iota
	HealMonster
	MonsConfusionEnd
	MonsExhaustionEnd
)

type monsterEvent struct {
	ERank   int
	NMons   int
	EAction monsterAction
}

func (mev *monsterEvent) Rank() int {
	return mev.ERank
}

func (mev *monsterEvent) Action(g *game) {
	switch mev.EAction {
	case MonsterTurn:
		mons := g.Monsters[mev.NMons]
		if mons.Exists() {
			mons.HandleTurn(g, mev)
		}
	case HealMonster:
		mons := g.Monsters[mev.NMons]
		if mons.Exists() {
			mons.Heal(g, mev)
		}
	case MonsConfusionEnd:
		mons := g.Monsters[mev.NMons]
		if mons.Exists() {
			mons.Statuses[MonsConfused] = 0
			g.Printf("The %s is no longer confused.", mons.Kind)
		}
	case MonsExhaustionEnd:
		mons := g.Monsters[mev.NMons]
		if mons.Exists() {
			mons.Statuses[MonsExhausted] = 0
		}
	}
}

func (mev *monsterEvent) Renew(g *game, delay int) {
	mev.ERank += delay
	g.PushEvent(mev)
}

type cloudAction int

const (
	CloudEnd cloudAction = iota
	ObstructionEnd
	FireProgression
)

type cloudEvent struct {
	ERank   int
	Pos     position
	EAction cloudAction
}

func (cev *cloudEvent) Rank() int {
	return cev.ERank
}

func (cev *cloudEvent) Action(g *game) {
	switch cev.EAction {
	case CloudEnd:
		delete(g.Clouds, cev.Pos)
		g.ComputeLOS()
	case ObstructionEnd:
		if !g.Player.LOS[cev.Pos] && g.Dungeon.Cell(cev.Pos).T == WallCell {
			g.UnknownDig[cev.Pos] = true
		} else {
			delete(g.TemporalWalls, cev.Pos)
		}
		if g.Dungeon.Cell(cev.Pos).T == FreeCell {
			break
		}
		g.Dungeon.SetCell(cev.Pos, FreeCell)
		g.MakeNoise(15, cev.Pos)
		g.Fog(cev.Pos, 1, &simpleEvent{ERank: cev.Rank()})
		g.ComputeLOS()
	case FireProgression:
		if _, ok := g.Clouds[cev.Pos]; !ok {
			break
		}
		g.BurnCreature(cev.Pos, cev)
		if RandInt(10) == 0 {
			delete(g.Clouds, cev.Pos)
			g.Fog(cev.Pos, 1, &simpleEvent{ERank: cev.Rank()})
			g.ComputeLOS()
			break
		}
		for _, pos := range g.Dungeon.FreeNeighbors(cev.Pos) {
			if RandInt(3) > 0 {
				continue
			}
			g.Burn(pos, cev)
		}
		cev.Renew(g, 10)
	}
}

func (g *game) BurnCreature(pos position, ev event) {
	mons := g.MonsterAt(pos)
	if mons.Exists() {
		mons.HP -= 1 + RandInt(10)
		if mons.HP <= 0 {
			g.PrintfStyled("%s is killed by the fire.", logPlayerHit, mons.Kind.Definite(true))
			g.HandleKill(mons, ev)
		} else {
			mons.MakeAwareIfHurt(g)
		}
	}
	if pos == g.Player.Pos {
		damage := 1 + RandInt(10)
		if damage > g.Player.HP {
			damage = 1 + RandInt(10)
		}
		g.Player.HP -= damage
		g.PrintfStyled("The fire burns you (%d dmg).", logMonsterHit, damage)
		if g.Player.HP+damage < 10 {
			g.Stats.TimesLucky++
		}
		g.StopAuto()
	}
}

type burn int

const (
	NoUnknownBurn burn = iota
	FoliageBurn
	DoorBurn
)

func (g *game) Burn(pos position, ev event) {
	if _, ok := g.Clouds[pos]; ok {
		return
	}
	_, okFungus := g.Fungus[pos]
	_, okDoor := g.Doors[pos]
	if !okFungus && !okDoor {
		return
	}
	g.Stats.Burns++
	brn := FoliageBurn
	delete(g.Fungus, pos)
	if _, ok := g.Doors[pos]; ok {
		delete(g.Doors, pos)
		brn = DoorBurn
		g.Print("The door vanishes in flames.")
	}
	g.Clouds[pos] = CloudFire
	if !g.Player.LOS[pos] {
		g.UnknownBurn[pos] = brn
	}
	g.PushEvent(&cloudEvent{ERank: ev.Rank() + 10, EAction: FireProgression, Pos: pos})
	g.BurnCreature(pos, ev)
}

func (cev *cloudEvent) Renew(g *game, delay int) {
	cev.ERank += delay
	g.PushEvent(cev)
}
<|endoftext|>


<commit_before>package raft

import "reflect"

// Callback list    args
// init::done
// state::changed   old, new string

// Javascript events style
// All methods are safe
// Since it is thread safe and only one action
// can take place at the time, adding, executing,
// removing callbacks will may not be in the order
// you want to.
// TODO: keep the global channel for global actions,
// like adding or removing events, but I should add
// a local channel to edit or execute events without
// blocking all the other events.
type Events struct {
  Callbacks map[string][]*Event
  C chan struct{}
}

type Event struct {
  Callback Callback
  Once bool
  Executed bool
  C chan struct{}
}

type Callback func(args ...interface{})

// .On appends a function to an array
// The function Callback should be pass as a reference
// ie: blih := func(args ...interface{}) {}
// sm.State.On("someState", blih)
// sm.State.Off("someState", blih)
// If it is passed like sm.State.On("someState", func(args ...interface{}){})
// the function will never be removable.
func (e *Events) On(name string, cb Callback) {
  e.C <- struct{}{}
  defer func() {
    <- e.C
  }()

  if cbs, found := e.Callbacks[name]; found {
    event := &Event{
      Callback: cb,
      Once: false,
      Executed: false,
    }

    e.Callbacks[name] = append(cbs, event)
  } else {
    e.Callbacks[name] = make([]*Event, 0, 0)

    event := &Event{
      Callback: cb,
      Once: false,
      Executed: false,
    }

    e.Callbacks[name] = append(e.Callbacks[name], event)
  }
}

// .Once appends a function to an array
// When the function is executed, it will be deferenced.
// The function Callback should be pass as a reference
// ie: blih := func(args ...interface{}) {}
// sm.State.Once("someState", blih)
// sm.State.Off("someState", blih)
// If it is passed like sm.State.Once("someState", func(args ...interface{}){})
// the function will never be removable.
func (e *Events) Once(name string, cb Callback) {
  e.C <- struct{}{}
  defer func() {
    <- e.C
  }()

  if cbs, found := e.Callbacks[name]; found {
    event := &Event{
      Callback: cb,
      Once: true,
      Executed: false,
    }

    e.Callbacks[name] = append(cbs, event)
  } else {
    e.Callbacks[name] = make([]*Event, 0, 0)

    event := &Event{
      Callback: cb,
      Once: true,
      Executed: false,
    }

    e.Callbacks[name] = append(e.Callbacks[name], event)
  }
}

// Remove a callback from the array
// The function Callback should be pass as a reference
// ie: blih := func(args ...interface{}) {}
// sm.State.On("someState", blih)
// sm.State.Off("someState", blih)
// If it is passed like sm.State.On("someState", func(args ...interface{}){})
// the function will never be removable.
func (e *Events) Off(name string, cb Callback) {
  e.C <- struct{}{}
  defer func() {
    <- e.C
  }()

  if cbs, found := e.Callbacks[name]; found {
    length := len(cbs)

    foundAt := -1

    for i, event := range cbs{
      if reflect.ValueOf(event.Callback).Pointer() == reflect.ValueOf(cb).Pointer() {
        foundAt = i
        break
      }
    }

    if foundAt == -1 {
      return
    }

    tmp := make([]*Event, length -1)

    index := 0

    for i := 0; i < length; i++ {
      // Compare two pointer value
      if reflect.ValueOf(cbs[i].Callback).Pointer() == reflect.ValueOf(cb).Pointer() {
        continue
      }

      tmp[index] = cbs[i]
      index = index + 1
    }

    e.Callbacks[name] = tmp
  }
}

// Execute async all the callbacks that was registered with .On
func (e *Events) Exec(name string, args ...interface{}) {
  e.C <- struct{}{}
  defer func() {
    <- e.C
  }()

  if cbs, found := e.Callbacks[name]; found {
    length := len(cbs)

    for i := 0; i < length; i++ {
      go func(event *Event) {
        if event.Once == true  && event.Executed == true {
        } else {
          event.Callback(args...)
          event.Executed = true

          if event.Once == true {
            e.Off(name, event.Callback)
          }
        }
      }(cbs[i])
    }
  }
}

// Execute sync all the callbacks that was registered with .On
func (e *Events) ExecSync(name string, args ...interface{}) {
  e.C <- struct{}{}
  defer func() {
    <- e.C
  }()

  if cbs, found := e.Callbacks[name]; found {
    length := len(cbs)

    for i := 0; i < length; i++ {
      event := cbs[i]

      if event.Once == true  && event.Executed == true {
      } else {
        event.Callback(args...)
        event.Executed = true

        if event.Once == true {
          e.Off(name, event.Callback)
        }
      }
    }
  }
}

// Creates a callback array
func NewEvents() *Events {
  e := &Events{
    Callbacks: make(map[string][]*Event, 0),
    C: make(chan struct{}, 1),
  }

  return e
}
<commit_msg>I'll see later about local locks<commit_after>package raft

import "reflect"

// Callback list    args
// init::done
// state::changed   old, new string

// Javascript events style
// All methods are safe
// Since it is thread safe and only one action
// can take place at the time, adding, executing,
// removing callbacks will may not be in the order
// you want to.
// TODO: keep the global channel for global actions,
// like adding or removing events, but I should add
// a local channel to edit or execute events without
// blocking all the other events.
type Events struct {
  Callbacks map[string][]*Event
  C chan struct{}
}

type Event struct {
  Callback Callback
  Once bool
  Executed bool
}

type Callback func(args ...interface{})

// .On appends a function to an array
// The function Callback should be pass as a reference
// ie: blih := func(args ...interface{}) {}
// sm.State.On("someState", blih)
// sm.State.Off("someState", blih)
// If it is passed like sm.State.On("someState", func(args ...interface{}){})
// the function will never be removable.
func (e *Events) On(name string, cb Callback) {
  e.C <- struct{}{}
  defer func() {
    <- e.C
  }()

  if cbs, found := e.Callbacks[name]; found {
    event := &Event{
      Callback: cb,
      Once: false,
      Executed: false,
    }

    e.Callbacks[name] = append(cbs, event)
  } else {
    e.Callbacks[name] = make([]*Event, 0, 0)

    event := &Event{
      Callback: cb,
      Once: false,
      Executed: false,
    }

    e.Callbacks[name] = append(e.Callbacks[name], event)
  }
}

// .Once appends a function to an array
// When the function is executed, it will be deferenced.
// The function Callback should be pass as a reference
// ie: blih := func(args ...interface{}) {}
// sm.State.Once("someState", blih)
// sm.State.Off("someState", blih)
// If it is passed like sm.State.Once("someState", func(args ...interface{}){})
// the function will never be removable.
func (e *Events) Once(name string, cb Callback) {
  e.C <- struct{}{}
  defer func() {
    <- e.C
  }()

  if cbs, found := e.Callbacks[name]; found {
    event := &Event{
      Callback: cb,
      Once: true,
      Executed: false,
    }

    e.Callbacks[name] = append(cbs, event)
  } else {
    e.Callbacks[name] = make([]*Event, 0, 0)

    event := &Event{
      Callback: cb,
      Once: true,
      Executed: false,
    }

    e.Callbacks[name] = append(e.Callbacks[name], event)
  }
}

// Remove a callback from the array
// The function Callback should be pass as a reference
// ie: blih := func(args ...interface{}) {}
// sm.State.On("someState", blih)
// sm.State.Off("someState", blih)
// If it is passed like sm.State.On("someState", func(args ...interface{}){})
// the function will never be removable.
func (e *Events) Off(name string, cb Callback) {
  e.C <- struct{}{}
  defer func() {
    <- e.C
  }()

  if cbs, found := e.Callbacks[name]; found {
    length := len(cbs)

    foundAt := -1

    for i, event := range cbs{
      if reflect.ValueOf(event.Callback).Pointer() == reflect.ValueOf(cb).Pointer() {
        foundAt = i
        break
      }
    }

    if foundAt == -1 {
      return
    }

    tmp := make([]*Event, length -1)

    index := 0

    for i := 0; i < length; i++ {
      // Compare two pointer value
      if reflect.ValueOf(cbs[i].Callback).Pointer() == reflect.ValueOf(cb).Pointer() {
        continue
      }

      tmp[index] = cbs[i]
      index = index + 1
    }

    e.Callbacks[name] = tmp
  }
}

// Execute async all the callbacks that was registered with .On
func (e *Events) Exec(name string, args ...interface{}) {
  e.C <- struct{}{}
  defer func() {
    <- e.C
  }()

  if cbs, found := e.Callbacks[name]; found {
    length := len(cbs)

    for i := 0; i < length; i++ {
      go func(event *Event) {
        if event.Once == true  && event.Executed == true {
        } else {
          event.Callback(args...)
          event.Executed = true

          if event.Once == true {
            e.Off(name, event.Callback)
          }
        }
      }(cbs[i])
    }
  }
}

// Execute sync all the callbacks that was registered with .On
func (e *Events) ExecSync(name string, args ...interface{}) {
  e.C <- struct{}{}
  defer func() {
    <- e.C
  }()

  if cbs, found := e.Callbacks[name]; found {
    length := len(cbs)

    for i := 0; i < length; i++ {
      event := cbs[i]

      if event.Once == true  && event.Executed == true {
      } else {
        event.Callback(args...)
        event.Executed = true

        if event.Once == true {
          e.Off(name, event.Callback)
        }
      }
    }
  }
}

// Creates a callback array
func NewEvents() *Events {
  e := &Events{
    Callbacks: make(map[string][]*Event, 0),
    C: make(chan struct{}, 1),
  }

  return e
}
<|endoftext|>


<commit_before>package vlc

// Event represents an event that can occur inside libvlc.
type Event int

// Media events.
const (
	// MediaMetaChanged is triggered when the metadata of a media item changes.
	MediaMetaChanged Event = iota

	// MediaSubItemAdded is triggered when a Subitem is added to a media item.
	MediaSubItemAdded

	// MediaDurationChanged is triggered when the duration
	// of a media item changes.
	MediaDurationChanged

	// MediaParsedChanged is triggered when the parsing state
	// of a media item changes.
	MediaParsedChanged

	// MediaFreed is triggered when a media item is freed.
	MediaFreed

	// MediaStateChanged is triggered when the state of the media item changes.
	MediaStateChanged

	// MediaSubItemTreeAdded is triggered when a Subitem tree is
	// added to a media item.
	MediaSubItemTreeAdded

	// MediaThumbnailGenerated is triggered when a thumbnail
	// generation is completed.
	MediaThumbnailGenerated
)

// Player events.
const (
	MediaPlayerMediaChanged Event = 0x100 + iota
	MediaPlayerNothingSpecial
	MediaPlayerOpening
	MediaPlayerBuffering
	MediaPlayerPlaying
	MediaPlayerPaused
	MediaPlayerStopped
	MediaPlayerForward
	MediaPlayerBackward
	MediaPlayerEndReached
	MediaPlayerEncounteredError
	MediaPlayerTimeChanged
	MediaPlayerPositionChanged
	MediaPlayerSeekableChanged
	MediaPlayerPausableChanged
	MediaPlayerTitleChanged
	MediaPlayerSnapshotTaken
	MediaPlayerLengthChanged
	MediaPlayerVout
	MediaPlayerScrambledChanged
	MediaPlayerESAdded
	MediaPlayerESDeleted
	MediaPlayerESSelected
	MediaPlayerCorked
	MediaPlayerUncorked
	MediaPlayerMuted
	MediaPlayerUnmuted
	MediaPlayerAudioVolume
	MediaPlayerAudioDevice
	MediaPlayerChapterChanged
)

// Media list events.
const (
	// MediaListItemAdded is triggered when a media item is added to a media list.
	MediaListItemAdded Event = 0x200 + iota

	// MediaListWillAddItem is triggered when a media item is about to get
	// added to a media list.
	MediaListWillAddItem

	// MediaListItemDeleted is triggered when a media item is deleted
	// from a media list.
	MediaListItemDeleted

	// MediaListWillDeleteItem is triggered when a media item is about to get
	// deleted from a media list.
	MediaListWillDeleteItem

	// MediaListEndReached is triggered when a media list has reached the end.
	MediaListEndReached
)

// Deprecated events.
const (
	MediaListViewItemAdded = 0x300 + iota
	MediaListViewWillAddItem
	MediaListViewItemDeleted
	MediaListViewWillDeleteItem
)

const (
	// MediaListPlayerPlayed is triggered when playback
	// of a media list player has started.
	MediaListPlayerPlayed = 0x400 + iota

	// MediaListPlayerNextItemSet is triggered when the current item
	// of a media list player has changed to a different item.
	MediaListPlayerNextItemSet

	// MediaListPlayerStopped is triggered when playback
	// of a media list player has stopped.
	MediaListPlayerStopped
)

// Deprecated events.
const (
	MediaDiscovererStarted Event = 0x500 + iota
	MediaDiscovererEnded
)

// Renderer events.
const (
	// RendererDiscovererItemAdded is triggered when a new renderer item is
	// found by a renderer discoverer. The renderer item is valid until deleted.
	RendererDiscovererItemAdded Event = 0x502 + iota

	// RendererDiscovererItemDeleted is triggered when a previously discovered
	// renderer item was deleted by a renderer discoverer. The renderer item
	// is no longer valid.
	RendererDiscovererItemDeleted
)

// VideoLAN Manager events.
const (
	VlmMediaAdded Event = 0x600 + iota
	VlmMediaRemoved
	VlmMediaChanged
	VlmMediaInstanceStarted
	VlmMediaInstanceStopped
	VlmMediaInstanceStatusInit
	VlmMediaInstanceStatusOpening
	VlmMediaInstanceStatusPlaying
	VlmMediaInstanceStatusPause
	VlmMediaInstanceStatusEnd
	VlmMediaInstanceStatusError
)
<commit_msg>Fix documentation of media list player events<commit_after>package vlc

// Event represents an event that can occur inside libvlc.
type Event int

// Media events.
const (
	// MediaMetaChanged is triggered when the metadata of a media item changes.
	MediaMetaChanged Event = iota

	// MediaSubItemAdded is triggered when a Subitem is added to a media item.
	MediaSubItemAdded

	// MediaDurationChanged is triggered when the duration
	// of a media item changes.
	MediaDurationChanged

	// MediaParsedChanged is triggered when the parsing state
	// of a media item changes.
	MediaParsedChanged

	// MediaFreed is triggered when a media item is freed.
	MediaFreed

	// MediaStateChanged is triggered when the state of the media item changes.
	MediaStateChanged

	// MediaSubItemTreeAdded is triggered when a Subitem tree is
	// added to a media item.
	MediaSubItemTreeAdded

	// MediaThumbnailGenerated is triggered when a thumbnail
	// generation is completed.
	MediaThumbnailGenerated
)

// Player events.
const (
	MediaPlayerMediaChanged Event = 0x100 + iota
	MediaPlayerNothingSpecial
	MediaPlayerOpening
	MediaPlayerBuffering
	MediaPlayerPlaying
	MediaPlayerPaused
	MediaPlayerStopped
	MediaPlayerForward
	MediaPlayerBackward
	MediaPlayerEndReached
	MediaPlayerEncounteredError
	MediaPlayerTimeChanged
	MediaPlayerPositionChanged
	MediaPlayerSeekableChanged
	MediaPlayerPausableChanged
	MediaPlayerTitleChanged
	MediaPlayerSnapshotTaken
	MediaPlayerLengthChanged
	MediaPlayerVout
	MediaPlayerScrambledChanged
	MediaPlayerESAdded
	MediaPlayerESDeleted
	MediaPlayerESSelected
	MediaPlayerCorked
	MediaPlayerUncorked
	MediaPlayerMuted
	MediaPlayerUnmuted
	MediaPlayerAudioVolume
	MediaPlayerAudioDevice
	MediaPlayerChapterChanged
)

// Media list events.
const (
	// MediaListItemAdded is triggered when a media item is added to a media list.
	MediaListItemAdded Event = 0x200 + iota

	// MediaListWillAddItem is triggered when a media item is about to get
	// added to a media list.
	MediaListWillAddItem

	// MediaListItemDeleted is triggered when a media item is deleted
	// from a media list.
	MediaListItemDeleted

	// MediaListWillDeleteItem is triggered when a media item is about to get
	// deleted from a media list.
	MediaListWillDeleteItem

	// MediaListEndReached is triggered when a media list has reached the end.
	MediaListEndReached
)

// Deprecated events.
const (
	MediaListViewItemAdded = 0x300 + iota
	MediaListViewWillAddItem
	MediaListViewItemDeleted
	MediaListViewWillDeleteItem
)

const (
	// MediaListPlayerPlayed is triggered when playback of the media list
	// of the list player has ended.
	MediaListPlayerPlayed = 0x400 + iota

	// MediaListPlayerNextItemSet is triggered when the current item
	// of a media list player has changed to a different item.
	MediaListPlayerNextItemSet

	// MediaListPlayerStopped is triggered when playback
	// of a media list player is stopped programmatically.
	MediaListPlayerStopped
)

// Deprecated events.
const (
	MediaDiscovererStarted Event = 0x500 + iota
	MediaDiscovererEnded
)

// Renderer events.
const (
	// RendererDiscovererItemAdded is triggered when a new renderer item is
	// found by a renderer discoverer. The renderer item is valid until deleted.
	RendererDiscovererItemAdded Event = 0x502 + iota

	// RendererDiscovererItemDeleted is triggered when a previously discovered
	// renderer item was deleted by a renderer discoverer. The renderer item
	// is no longer valid.
	RendererDiscovererItemDeleted
)

// VideoLAN Manager events.
const (
	VlmMediaAdded Event = 0x600 + iota
	VlmMediaRemoved
	VlmMediaChanged
	VlmMediaInstanceStarted
	VlmMediaInstanceStopped
	VlmMediaInstanceStatusInit
	VlmMediaInstanceStatusOpening
	VlmMediaInstanceStatusPlaying
	VlmMediaInstanceStatusPause
	VlmMediaInstanceStatusEnd
	VlmMediaInstanceStatusError
)
<|endoftext|>


<commit_before>// Copyright 2014 Arne Roomann-Kurrik
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package twodee

import (
	glfw "github.com/go-gl/glfw/v3.1/glfw"
)

type EventHandler struct {
	Events chan Event
	window *glfw.Window
}

func NewEventHandler(w *glfw.Window) (e *EventHandler) {
	e = &EventHandler{
		Events: make(chan Event, 100),
		window: w,
	}
	// See http://www.glfw.org/docs/latest/input.html#input_keyboard
	w.SetInputMode(glfw.StickyKeysMode, 1)
	w.SetCursorPosCallback(e.onMouseMove)
	w.SetKeyCallback(e.onKey)
	w.SetMouseButtonCallback(e.onMouseButton)
	return
}

func (e *EventHandler) GetKey(code KeyCode) Action {
	return Action(e.window.GetKey(glfw.Key(code)))
}

func (e *EventHandler) Poll() {
	glfw.PollEvents()
}

func (e *EventHandler) onKey(w *glfw.Window, key glfw.Key, scancode int, action glfw.Action, mods glfw.ModifierKey) {
	event := &KeyEvent{
		Code: KeyCode(key),
		Type: Action(action),
	}
	e.enqueue(event)
}

func (e *EventHandler) onMouseMove(w *glfw.Window, xoff float64, yoff float64) {
	event := &MouseMoveEvent{
		X: float32(xoff),
		Y: float32(yoff),
	}
	e.enqueue(event)
}

func (e *EventHandler) onMouseButton(w *glfw.Window, button glfw.MouseButton, action glfw.Action, mod glfw.ModifierKey) {
	event := &MouseButtonEvent{
		Type:   Action(action),
		Button: MouseButton(button),
	}
	e.enqueue(event)
}

func (e *EventHandler) enqueue(event Event) {
	select {
	case e.Events <- event:
		// Added to events
	default:
		// Events buffer is too full, not being read.
		// Drop the event on the floor.
		// TODO: Warn?
	}
}

type Event interface{}

type MouseMoveEvent struct {
	X float32
	Y float32
}

type MouseButtonEvent struct {
	Button MouseButton
	Type   Action
}

type KeyEvent struct {
	Code KeyCode
	Type Action
}

type KeyCode int

const (
	KeyUp     = KeyCode(glfw.KeyUp)
	KeyDown   = KeyCode(glfw.KeyDown)
	KeyLeft   = KeyCode(glfw.KeyLeft)
	KeyRight  = KeyCode(glfw.KeyRight)
	KeyEnter  = KeyCode(glfw.KeyEnter)
	KeyEscape = KeyCode(glfw.KeyEscape)
	KeySpace  = KeyCode(glfw.KeySpace)
	KeyA      = KeyCode(glfw.KeyA)
	KeyB      = KeyCode(glfw.KeyB)
	KeyC      = KeyCode(glfw.KeyC)
	KeyD      = KeyCode(glfw.KeyD)
	KeyE      = KeyCode(glfw.KeyE)
	KeyF      = KeyCode(glfw.KeyF)
	KeyG      = KeyCode(glfw.KeyG)
	KeyH      = KeyCode(glfw.KeyH)
	KeyI      = KeyCode(glfw.KeyI)
	KeyJ      = KeyCode(glfw.KeyJ)
	KeyK      = KeyCode(glfw.KeyK)
	KeyL      = KeyCode(glfw.KeyL)
	KeyM      = KeyCode(glfw.KeyM)
	KeyN      = KeyCode(glfw.KeyN)
	KeyO      = KeyCode(glfw.KeyO)
	KeyP      = KeyCode(glfw.KeyP)
	KeyQ      = KeyCode(glfw.KeyQ)
	KeyR      = KeyCode(glfw.KeyR)
	KeyS      = KeyCode(glfw.KeyS)
	KeyT      = KeyCode(glfw.KeyT)
	KeyU      = KeyCode(glfw.KeyU)
	KeyV      = KeyCode(glfw.KeyV)
	KeyW      = KeyCode(glfw.KeyW)
	KeyX      = KeyCode(glfw.KeyX)
	KeyY      = KeyCode(glfw.KeyY)
	KeyZ      = KeyCode(glfw.KeyZ)
	Key1      = KeyCode(glfw.Key1)
	Key2      = KeyCode(glfw.Key2)
	Key3      = KeyCode(glfw.Key3)
	Key4      = KeyCode(glfw.Key4)
	Key5      = KeyCode(glfw.Key5)
	Key6      = KeyCode(glfw.Key6)
	Key7      = KeyCode(glfw.Key7)
	Key8      = KeyCode(glfw.Key8)
	Key9      = KeyCode(glfw.Key9)
	Key0      = KeyCode(glfw.Key0)
)

type Action int

const (
	Release = Action(glfw.Release)
	Press   = Action(glfw.Press)
	Repeat  = Action(glfw.Repeat)
)

type MouseButton int

const (
	MouseButton1      = MouseButton(glfw.MouseButton1)
	MouseButton2      = MouseButton(glfw.MouseButton2)
	MouseButton3      = MouseButton(glfw.MouseButton3)
	MouseButton4      = MouseButton(glfw.MouseButton4)
	MouseButton5      = MouseButton(glfw.MouseButton5)
	MouseButton6      = MouseButton(glfw.MouseButton6)
	MouseButton7      = MouseButton(glfw.MouseButton7)
	MouseButton8      = MouseButton(glfw.MouseButton8)
	MouseButtonLast   = MouseButton(glfw.MouseButtonLast)
	MouseButtonLeft   = MouseButton(glfw.MouseButtonLeft)
	MouseButtonRight  = MouseButton(glfw.MouseButtonRight)
	MouseButtonMiddle = MouseButton(glfw.MouseButtonMiddle)
)
<commit_msg>Add joystick methods<commit_after>// Copyright 2014 Arne Roomann-Kurrik
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package twodee

import (
	glfw "github.com/go-gl/glfw/v3.1/glfw"
)

type EventHandler struct {
	Events chan Event
	window *glfw.Window
}

func NewEventHandler(w *glfw.Window) (e *EventHandler) {
	e = &EventHandler{
		Events: make(chan Event, 100),
		window: w,
	}
	// See http://www.glfw.org/docs/latest/input.html#input_keyboard
	w.SetInputMode(glfw.StickyKeysMode, 1)
	w.SetCursorPosCallback(e.onMouseMove)
	w.SetKeyCallback(e.onKey)
	w.SetMouseButtonCallback(e.onMouseButton)
	return
}

func (e *EventHandler) GetKey(code KeyCode) Action {
	return Action(e.window.GetKey(glfw.Key(code)))
}

func (e *EventHandler) JoystickPresent(joystick Joystick) bool {
	return glfw.JoystickPresent(glfw.Joystick(joystick))
}

func (e *EventHandler) JoystickAxes(joystick Joystick) []float32 {
	return glfw.GetJoystickAxes(glfw.Joystick(joystick))
}

func (e *EventHandler) JoystickButtons(joystick Joystick) []byte {
	return glfw.GetJoystickButtons(glfw.Joystick(joystick))
}

func (e *EventHandler) Poll() {
	glfw.PollEvents()
}

func (e *EventHandler) onKey(w *glfw.Window, key glfw.Key, scancode int, action glfw.Action, mods glfw.ModifierKey) {
	event := &KeyEvent{
		Code: KeyCode(key),
		Type: Action(action),
	}
	e.enqueue(event)
}

func (e *EventHandler) onMouseMove(w *glfw.Window, xoff float64, yoff float64) {
	event := &MouseMoveEvent{
		X: float32(xoff),
		Y: float32(yoff),
	}
	e.enqueue(event)
}

func (e *EventHandler) onMouseButton(w *glfw.Window, button glfw.MouseButton, action glfw.Action, mod glfw.ModifierKey) {
	event := &MouseButtonEvent{
		Type:   Action(action),
		Button: MouseButton(button),
	}
	e.enqueue(event)
}

func (e *EventHandler) enqueue(event Event) {
	select {
	case e.Events <- event:
		// Added to events
	default:
		// Events buffer is too full, not being read.
		// Drop the event on the floor.
		// TODO: Warn?
	}
}

type Event interface{}

type MouseMoveEvent struct {
	X float32
	Y float32
}

type MouseButtonEvent struct {
	Button MouseButton
	Type   Action
}

type KeyEvent struct {
	Code KeyCode
	Type Action
}

type KeyCode int

const (
	KeyUp     = KeyCode(glfw.KeyUp)
	KeyDown   = KeyCode(glfw.KeyDown)
	KeyLeft   = KeyCode(glfw.KeyLeft)
	KeyRight  = KeyCode(glfw.KeyRight)
	KeyEnter  = KeyCode(glfw.KeyEnter)
	KeyEscape = KeyCode(glfw.KeyEscape)
	KeySpace  = KeyCode(glfw.KeySpace)
	KeyA      = KeyCode(glfw.KeyA)
	KeyB      = KeyCode(glfw.KeyB)
	KeyC      = KeyCode(glfw.KeyC)
	KeyD      = KeyCode(glfw.KeyD)
	KeyE      = KeyCode(glfw.KeyE)
	KeyF      = KeyCode(glfw.KeyF)
	KeyG      = KeyCode(glfw.KeyG)
	KeyH      = KeyCode(glfw.KeyH)
	KeyI      = KeyCode(glfw.KeyI)
	KeyJ      = KeyCode(glfw.KeyJ)
	KeyK      = KeyCode(glfw.KeyK)
	KeyL      = KeyCode(glfw.KeyL)
	KeyM      = KeyCode(glfw.KeyM)
	KeyN      = KeyCode(glfw.KeyN)
	KeyO      = KeyCode(glfw.KeyO)
	KeyP      = KeyCode(glfw.KeyP)
	KeyQ      = KeyCode(glfw.KeyQ)
	KeyR      = KeyCode(glfw.KeyR)
	KeyS      = KeyCode(glfw.KeyS)
	KeyT      = KeyCode(glfw.KeyT)
	KeyU      = KeyCode(glfw.KeyU)
	KeyV      = KeyCode(glfw.KeyV)
	KeyW      = KeyCode(glfw.KeyW)
	KeyX      = KeyCode(glfw.KeyX)
	KeyY      = KeyCode(glfw.KeyY)
	KeyZ      = KeyCode(glfw.KeyZ)
	Key1      = KeyCode(glfw.Key1)
	Key2      = KeyCode(glfw.Key2)
	Key3      = KeyCode(glfw.Key3)
	Key4      = KeyCode(glfw.Key4)
	Key5      = KeyCode(glfw.Key5)
	Key6      = KeyCode(glfw.Key6)
	Key7      = KeyCode(glfw.Key7)
	Key8      = KeyCode(glfw.Key8)
	Key9      = KeyCode(glfw.Key9)
	Key0      = KeyCode(glfw.Key0)
)

type Joystick int

const (
	Joystick1 = Joystick(glfw.Joystick1)
)

type Action int

const (
	Release = Action(glfw.Release)
	Press   = Action(glfw.Press)
	Repeat  = Action(glfw.Repeat)
)

type MouseButton int

const (
	MouseButton1      = MouseButton(glfw.MouseButton1)
	MouseButton2      = MouseButton(glfw.MouseButton2)
	MouseButton3      = MouseButton(glfw.MouseButton3)
	MouseButton4      = MouseButton(glfw.MouseButton4)
	MouseButton5      = MouseButton(glfw.MouseButton5)
	MouseButton6      = MouseButton(glfw.MouseButton6)
	MouseButton7      = MouseButton(glfw.MouseButton7)
	MouseButton8      = MouseButton(glfw.MouseButton8)
	MouseButtonLast   = MouseButton(glfw.MouseButtonLast)
	MouseButtonLeft   = MouseButton(glfw.MouseButtonLeft)
	MouseButtonRight  = MouseButton(glfw.MouseButtonRight)
	MouseButtonMiddle = MouseButton(glfw.MouseButtonMiddle)
)
<|endoftext|>


<commit_before>package gocql

import (
	"log"
	"net"
	"sync"
	"time"
)

type eventDeouncer struct {
	name   string
	timer  *time.Timer
	mu     sync.Mutex
	events []frame

	callback func([]frame)
	quit     chan struct{}
}

func newEventDeouncer(name string, eventHandler func([]frame)) *eventDeouncer {
	e := &eventDeouncer{
		name:     name,
		quit:     make(chan struct{}),
		timer:    time.NewTimer(eventDebounceTime),
		callback: eventHandler,
	}
	e.timer.Stop()
	go e.flusher()

	return e
}

func (e *eventDeouncer) stop() {
	e.quit <- struct{}{} // sync with flusher
	close(e.quit)
}

func (e *eventDeouncer) flusher() {
	for {
		select {
		case <-e.timer.C:
			e.mu.Lock()
			e.flush()
			e.mu.Unlock()
		case <-e.quit:
			return
		}
	}
}

const (
	eventBufferSize   = 1000
	eventDebounceTime = 1 * time.Second
)

// flush must be called with mu locked
func (e *eventDeouncer) flush() {
	if len(e.events) == 0 {
		return
	}

	// if the flush interval is faster than the callback then we will end up calling
	// the callback multiple times, probably a bad idea. In this case we could drop
	// frames?
	go e.callback(e.events)
	e.events = make([]frame, 0, eventBufferSize)
}

func (e *eventDeouncer) debounce(frame frame) {
	e.mu.Lock()
	e.timer.Reset(eventDebounceTime)

	// TODO: probably need a warning to track if this threshold is too low
	if len(e.events) < eventBufferSize {
		e.events = append(e.events, frame)
	} else {
		log.Printf("%s: buffer full, dropping event frame: %s", e.name, frame)
	}

	e.mu.Unlock()
}

func (s *Session) handleNodeEvent(frames []frame) {
	type nodeEvent struct {
		change string
		host   net.IP
		port   int
	}

	events := make(map[string]*nodeEvent)

	for _, frame := range frames {
		// TODO: can we be sure the order of events in the buffer is correct?
		switch f := frame.(type) {
		case *topologyChangeEventFrame:
			event, ok := events[f.host.String()]
			if !ok {
				event = &nodeEvent{change: f.change, host: f.host, port: f.port}
				events[f.host.String()] = event
			}
			event.change = f.change

		case *statusChangeEventFrame:
			event, ok := events[f.host.String()]
			if !ok {
				event = &nodeEvent{change: f.change, host: f.host, port: f.port}
				events[f.host.String()] = event
			}
			event.change = f.change
		}
	}

	for _, f := range events {
		switch f.change {
		case "NEW_NODE":
			s.handleNewNode(f.host, f.port)
		case "REMOVED_NODE":
			s.handleRemovedNode(f.host, f.port)
		case "MOVED_NODE":
		// java-driver handles this, not mentioned in the spec
		// TODO(zariel): refresh token map
		case "UP":
			s.handleNodeUp(f.host, f.port)
		case "DOWN":
			s.handleNodeDown(f.host, f.port)
		}
	}
}

func (s *Session) handleEvent(framer *framer) {
	// TODO(zariel): need to debounce events frames, and possible also events
	defer framerPool.Put(framer)

	frame, err := framer.parseFrame()
	if err != nil {
		// TODO: logger
		log.Printf("gocql: unable to parse event frame: %v\n", err)
		return
	}

	// TODO: handle medatadata events
	switch f := frame.(type) {
	case *schemaChangeKeyspace:
	case *schemaChangeFunction:
	case *schemaChangeTable:
	case *topologyChangeEventFrame, *statusChangeEventFrame:
		s.nodeEvents.debounce(frame)
	default:
		log.Printf("gocql: invalid event frame (%T): %v\n", f, f)
	}

}

func (s *Session) handleNewNode(host net.IP, port int) {
	// TODO(zariel): need to be able to filter discovered nodes

	var hostInfo *HostInfo
	if s.control != nil {
		var err error
		hostInfo, err = s.control.fetchHostInfo(host, port)
		if err != nil {
			log.Printf("gocql: events: unable to fetch host info for %v: %v\n", host, err)
			return
		}

	} else {
		hostInfo = &HostInfo{peer: host.String(), port: port, state: NodeUp}
	}

	// should this handle token moving?
	if existing, ok := s.ring.addHostIfMissing(hostInfo); !ok {
		existing.update(hostInfo)
		hostInfo = existing
	}

	s.pool.addHost(hostInfo)

	if s.control != nil {
		s.hostSource.refreshRing()
	}
}

func (s *Session) handleRemovedNode(ip net.IP, port int) {
	// we remove all nodes but only add ones which pass the filter
	addr := ip.String()
	s.pool.removeHost(addr)
	s.ring.removeHost(addr)

	s.hostSource.refreshRing()
}

func (s *Session) handleNodeUp(ip net.IP, port int) {
	addr := ip.String()
	host := s.ring.getHost(addr)
	if host != nil {
		host.setState(NodeUp)
		s.pool.hostUp(host)
		return
	}

	// TODO: this could infinite loop
	s.handleNewNode(ip, port)
}

func (s *Session) handleNodeDown(ip net.IP, port int) {
	addr := ip.String()
	host := s.ring.getHost(addr)
	if host != nil {
		host.setState(NodeDown)
	}

	s.pool.hostDown(addr)
}
<commit_msg>log incoming events<commit_after>package gocql

import (
	"log"
	"net"
	"sync"
	"time"
)

type eventDeouncer struct {
	name   string
	timer  *time.Timer
	mu     sync.Mutex
	events []frame

	callback func([]frame)
	quit     chan struct{}
}

func newEventDeouncer(name string, eventHandler func([]frame)) *eventDeouncer {
	e := &eventDeouncer{
		name:     name,
		quit:     make(chan struct{}),
		timer:    time.NewTimer(eventDebounceTime),
		callback: eventHandler,
	}
	e.timer.Stop()
	go e.flusher()

	return e
}

func (e *eventDeouncer) stop() {
	e.quit <- struct{}{} // sync with flusher
	close(e.quit)
}

func (e *eventDeouncer) flusher() {
	for {
		select {
		case <-e.timer.C:
			e.mu.Lock()
			e.flush()
			e.mu.Unlock()
		case <-e.quit:
			return
		}
	}
}

const (
	eventBufferSize   = 1000
	eventDebounceTime = 1 * time.Second
)

// flush must be called with mu locked
func (e *eventDeouncer) flush() {
	if len(e.events) == 0 {
		return
	}

	// if the flush interval is faster than the callback then we will end up calling
	// the callback multiple times, probably a bad idea. In this case we could drop
	// frames?
	go e.callback(e.events)
	e.events = make([]frame, 0, eventBufferSize)
}

func (e *eventDeouncer) debounce(frame frame) {
	e.mu.Lock()
	e.timer.Reset(eventDebounceTime)

	// TODO: probably need a warning to track if this threshold is too low
	if len(e.events) < eventBufferSize {
		e.events = append(e.events, frame)
	} else {
		log.Printf("%s: buffer full, dropping event frame: %s", e.name, frame)
	}

	e.mu.Unlock()
}

func (s *Session) handleNodeEvent(frames []frame) {
	type nodeEvent struct {
		change string
		host   net.IP
		port   int
	}

	events := make(map[string]*nodeEvent)

	for _, frame := range frames {
		// TODO: can we be sure the order of events in the buffer is correct?
		switch f := frame.(type) {
		case *topologyChangeEventFrame:
			event, ok := events[f.host.String()]
			if !ok {
				event = &nodeEvent{change: f.change, host: f.host, port: f.port}
				events[f.host.String()] = event
			}
			event.change = f.change

		case *statusChangeEventFrame:
			event, ok := events[f.host.String()]
			if !ok {
				event = &nodeEvent{change: f.change, host: f.host, port: f.port}
				events[f.host.String()] = event
			}
			event.change = f.change
		}
	}

	for _, f := range events {
		switch f.change {
		case "NEW_NODE":
			s.handleNewNode(f.host, f.port)
		case "REMOVED_NODE":
			s.handleRemovedNode(f.host, f.port)
		case "MOVED_NODE":
		// java-driver handles this, not mentioned in the spec
		// TODO(zariel): refresh token map
		case "UP":
			s.handleNodeUp(f.host, f.port)
		case "DOWN":
			s.handleNodeDown(f.host, f.port)
		}
	}
}

func (s *Session) handleEvent(framer *framer) {
	// TODO(zariel): need to debounce events frames, and possible also events
	defer framerPool.Put(framer)

	frame, err := framer.parseFrame()
	if err != nil {
		// TODO: logger
		log.Printf("gocql: unable to parse event frame: %v\n", err)
		return
	}
	log.Println(frame)

	// TODO: handle medatadata events
	switch f := frame.(type) {
	case *schemaChangeKeyspace:
	case *schemaChangeFunction:
	case *schemaChangeTable:
	case *topologyChangeEventFrame, *statusChangeEventFrame:
		s.nodeEvents.debounce(frame)
	default:
		log.Printf("gocql: invalid event frame (%T): %v\n", f, f)
	}

}

func (s *Session) handleNewNode(host net.IP, port int) {
	// TODO(zariel): need to be able to filter discovered nodes

	var hostInfo *HostInfo
	if s.control != nil {
		var err error
		hostInfo, err = s.control.fetchHostInfo(host, port)
		if err != nil {
			log.Printf("gocql: events: unable to fetch host info for %v: %v\n", host, err)
			return
		}

	} else {
		hostInfo = &HostInfo{peer: host.String(), port: port, state: NodeUp}
	}

	// should this handle token moving?
	if existing, ok := s.ring.addHostIfMissing(hostInfo); !ok {
		existing.update(hostInfo)
		hostInfo = existing
	}

	s.pool.addHost(hostInfo)

	if s.control != nil {
		s.hostSource.refreshRing()
	}
}

func (s *Session) handleRemovedNode(ip net.IP, port int) {
	// we remove all nodes but only add ones which pass the filter
	addr := ip.String()
	s.pool.removeHost(addr)
	s.ring.removeHost(addr)

	s.hostSource.refreshRing()
}

func (s *Session) handleNodeUp(ip net.IP, port int) {
	addr := ip.String()
	host := s.ring.getHost(addr)
	if host != nil {
		host.setState(NodeUp)
		s.pool.hostUp(host)
		return
	}

	// TODO: this could infinite loop
	s.handleNewNode(ip, port)
}

func (s *Session) handleNodeDown(ip net.IP, port int) {
	addr := ip.String()
	host := s.ring.getHost(addr)
	if host != nil {
		host.setState(NodeDown)
	}

	s.pool.hostDown(addr)
}
<|endoftext|>