이거 정말 리포에 설명도 대충되어 있고 자바 사용자들 특유의 번잡하고 의미 불명의 코드들로 설명해두어서 진 좀 빠졌습니다.

 

권한 측은 모두 허용으로 되어있기 때문에 서드파티 스크립트 등의 호출을 허용하는 경우 등, 보안상 신경을 써야 한다면 수정해서 사용해야 합니다:

gist.github.com/SeaniaTwix/ca0fdc751da93c84fd121b5b8e300b27

 

graalvm_load_js.kt

GitHub Gist: instantly share code, notes, and snippets.

gist.github.com

이제 가장 중요한 엔트리 파일인데, graalvm의 Context를 사용할 때는 기존 jvm의 Invocable처럼 invokeFunction 메서드가 없다는 것입니다. 일단 함수를 실행하려면 가장 마지막에 함수를 지정해야 합니다. 이때 이 익명 함수는 순수 값으로 가장 마지막에 존재해야 합니다. 즉 export 식이어서도 안 되고 const나 let으로 지정할 경우 이 또한 식(expression)이지만 값(value)이 아니므로 Value 값에서 invoke 할 수 없습니다. 단, 초기화를 하지 않는, 기존 값에 재할당 하는 식(expression)은 es 문법상 그 자체가 식의 결과 값을 반환하므로 가능합니다.

 

혹은 es6의 arrow 함수를 사용해도 됩니다. 개인적으로 arrow 함수를 사용하길 선호하는 편이지만 이 경우 문맥상 덩그러니 있는 것 같아서  아래에서는 사용하지 않았습니다.

또, 아래 코드에서는 굳이 가장 마지막에 추가적인 함수를 넣지 않고 최상위 코드 레이어에서 호출하는 라인만 함수 정의 위로 올려줘도 됩니다만, 함수 호출이 마지막에 와서는 안 된다는 것을 알리기 위해 고의적으로 작성하였습니다.

 

gist.github.com/SeaniaTwix/d703b484ced9328b43c85265fbd1d7b5

 

main.js

GitHub Gist: instantly share code, notes, and snippets.

gist.github.com

그 이외에는 다를 게 없습니다. 모듈 파일은 일반적인 esm와 동일합니다:

gist.github.com/SeaniaTwix/083f8a30ff2bb8e11c9828167017a7c1

 

world.js

GitHub Gist: instantly share code, notes, and snippets.

gist.github.com

위 코드들을 실행하면 다음과 같이 출력됩니다.

inside: hello world
outside: hello world

추가로 Value의 invokeMember 메서드로 첫 번째 인자에 이름을 문자열 값으로 넣어 호출하는 방법도 있습니다만 js 쪽 코드가 더 골때려집니다. 결국 위에서 설명한 방식보다 더 번잡합니다.

class ScriptApp {

    static run(...args) {
        print(...args)
    };

}

ScriptApp // 여기에 주목

 

함수 값 처럼 클래스 객체를 그대로 마지막에 써주어야 합니다. 그냥 class 정의로 끝나면 graalvm은 eval의 반환 값을 undefined로 반환하거나 가장 마지막에 정의된 함수 객체를 반환합니다.

 

여러모로 방식이 골때리네요.

 

가장 무난한 방법은 그냥 호출할 함수를 익명으로 가장 아래에 정의하거나 (정말 최고의 방법), 애당초 Context를 초기화 할 때 빌더에서 environment에 값을 미리 넣어주고 함수 정의 없이 최상위 문맥에서 코드를 실행하는 방식입니다만, 이는 스크립트 소스에서 env로 할당되는 값이 무엇이 있는지 어디서 오는지 알기 힘들 뿐더러 구문을 잘못 읽을 여지를 주기에 개인적으로 선호하는 방식은 아닙니다.

또한 타입스크립트를 사용 중인데 어지간해서는 declare var를 피하려고 하고 있기 때문에 저는 이 방식은 사용하지 않고 있습니다.

 

하여튼 이런 방식 말고 뭔가 더 있을 것 같은데 아직 모르겠습니다. 문맥상 마지막에 온 것만으로 반환 값이 되는 건 OCaml 같은 ML 계열 언어에서 많이 쓰이므로 어색하진 않고 좋아하는 계열의 언어입니다만, 코드의 최종 문맥상 마지막 값을 호스트로 반환해서 사용하는 건 ecmascript와 java 계열 언어 디자인상 어울리지 않는 것 같습니다. 뭐, graaljs 설계자는 무언가 이유가 있어서 이렇게 만들었겠죠.

 

그리고 자바 코드를 일일이 뒤져보는 건 진짜 고통인지라 요 1~2 시간 본 걸로도 지치네요.

 

이만 끝.

'공부 > JavaScript' 카테고리의 다른 글

[MapleStory] 플레이어 스크립트  (0) 2014.09.09

socket을 잘 설정하고 uuid도 맞고 서버 측 코드에서 서비스 등록도 잘 해도 device로부터 getUuids()를 줘도 반영이 안 되고, 안드로이드 측에서 connect()만 실행하면

 

java.io.IOException: read failed, socket might closed or timeout, read ret: -1

 

위와 같은 exception 나면서 연결이 안 되는 경우가 있는데 양측 기기에서 전부 페어링 해제를 하자

그 다음 서버를 실행하고(물론 sdp가 등록 되도록 해야 함) 페어링을 해주자.

 

이제 fetchUuidsWithSdp()를 실행하고 getUuids() 결과 값을 받아보면 추가되어있는 것을 볼 수 있다.

연결도 잘 될 것이다.

ERROR: Exception:
Traceback (most recent call last):
  File "c:\users\fezsi\scoop\apps\python27\current\lib\site-packages\pip\_internal\cli\base_command.py", line 188, in main
    status = self.run(options, args)
  File "c:\users\fezsi\scoop\apps\python27\current\lib\site-packages\pip\_internal\commands\install.py", line 345, in run
    resolver.resolve(requirement_set)
  File "c:\users\fezsi\scoop\apps\python27\current\lib\site-packages\pip\_internal\legacy_resolve.py", line 196, in resolve
    self._resolve_one(requirement_set, req)
  File "c:\users\fezsi\scoop\apps\python27\current\lib\site-packages\pip\_internal\legacy_resolve.py", line 359, in _resolve_one
    abstract_dist = self._get_abstract_dist_for(req_to_install)
  File "c:\users\fezsi\scoop\apps\python27\current\lib\site-packages\pip\_internal\legacy_resolve.py", line 307, in _get_abstract_dist_for
    self.require_hashes
  File "c:\users\fezsi\scoop\apps\python27\current\lib\site-packages\pip\_internal\operations\prepare.py", line 134, in prepare_linked_requirement
    req.populate_link(finder, upgrade_allowed, require_hashes)
  File "c:\users\fezsi\scoop\apps\python27\current\lib\site-packages\pip\_internal\req\req_install.py", line 211, in populate_link
    self.link = finder.find_requirement(self, upgrade)
  File "c:\users\fezsi\scoop\apps\python27\current\lib\site-packages\pip\_internal\index.py", line 1201, in find_requirement
    req.name, specifier=req.specifier, hashes=hashes,
  File "c:\users\fezsi\scoop\apps\python27\current\lib\site-packages\pip\_internal\index.py", line 1183, in find_candidates
    candidates = self.find_all_candidates(project_name)
  File "c:\users\fezsi\scoop\apps\python27\current\lib\site-packages\pip\_internal\index.py", line 1128, in find_all_candidates
    for page in self._get_pages(url_locations, project_name):
  File "c:\users\fezsi\scoop\apps\python27\current\lib\site-packages\pip\_internal\index.py", line 1282, in _get_pages
    page = _get_html_page(location, session=self.session)
  File "c:\users\fezsi\scoop\apps\python27\current\lib\site-packages\pip\_internal\index.py", line 234, in _get_html_page
    resp = _get_html_response(url, session=session)
  File "c:\users\fezsi\scoop\apps\python27\current\lib\site-packages\pip\_internal\index.py", line 182, in _get_html_response
    "Cache-Control": "max-age=0",
  File "c:\users\fezsi\scoop\apps\python27\current\lib\site-packages\pip\_vendor\requests\sessions.py", line 546, in get
    return self.request('GET', url, **kwargs)
  File "c:\users\fezsi\scoop\apps\python27\current\lib\site-packages\pip\_internal\download.py", line 624, in request
    return super(PipSession, self).request(method, url, *args, **kwargs)
  File "c:\users\fezsi\scoop\apps\python27\current\lib\site-packages\pip\_vendor\requests\sessions.py", line 524, in request
    prep.url, proxies, stream, verify, cert
  File "c:\users\fezsi\scoop\apps\python27\current\lib\site-packages\pip\_vendor\requests\sessions.py", line 700, in merge_environment_settings
    env_proxies = get_environ_proxies(url, no_proxy=no_proxy)
  File "c:\users\fezsi\scoop\apps\python27\current\lib\site-packages\pip\_vendor\requests\utils.py", line 761, in get_environ_proxies
    if should_bypass_proxies(url, no_proxy=no_proxy):
  File "c:\users\fezsi\scoop\apps\python27\current\lib\site-packages\pip\_vendor\requests\utils.py", line 745, in should_bypass_proxies
    bypass = proxy_bypass(parsed.hostname)
  File "c:\users\fezsi\scoop\apps\python27\current\lib\site-packages\pip\_vendor\requests\utils.py", line 95, in proxy_bypass
    return proxy_bypass_registry(host)
  File "c:\users\fezsi\scoop\apps\python27\current\lib\site-packages\pip\_vendor\requests\utils.py", line 61, in proxy_bypass_registry
    'ProxyEnable')[0])
ValueError: invalid literal for int() with base 10: ''

일단 내가 받은 에러 메시지는 이렇다 

 

프록시 쪽을 건든 기억이 있긴 한데 워낙 오래전 일이라 잘 기억 나지 않는다

그래서 ssl 관련 에러인 줄 알았다

 

단순하게 해결하는 방법은 그냥 pip 코드에서 프록시 설정 체크를 안 하도록 하면 된다.

다음과 같은 디렉토리/패스로 이동하자

 

> cd ...python-path/lib/site-packages/pip/_vendor/requests

 

이제 adapters.py를 열고 proxy = select_proxy를 검색한다. get_connection 함수에 있다.

 

해당 라인을 proxy = False로 변경한다.

 

이제 pip 사용하면 정상 작동 한다.

 

끝 

일반적으로 사용자 액션 없이 데이터 수정을 해도 뷰에 바로 반영이 안 되기 때문에 다음과 같이 해야 한다.

 

class DataModel : ObservableObject {
    @Published public var state: Int
    
    init(_ data: Int) {
		self.state = data
    }
}

struct ContentView: View {
    @ObservedObject var data: DataModel
    
    var body: some View {
    	HStack {
        	Text("\(self.data.state)")
        }
    }
}

class SceneDelegate: UIResponder, UIWindowSceneDelegate {
    var window: UIWindow?

    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
    	let dataModel = DataModel(0)
        let contentView = ContentView(data: dataModel)
        
        DispatchQueue.global().async {
            while true {
                DispatchQueue.main.async { // 업데이트는 꼭 메인 스레드에서 해야 합니다.
                    contentView.data.objectWillChange.send() // 먼저 실행
                }
                _ = PseudoUpdater.refresh(dataModel) // pseudo: 업데이트 코드 실행 
                usleep(500_000)
            }
        }

        if let windowScene = scene as? UIWindowScene {
            let window = UIWindow(windowScene: windowScene)
            window.rootViewController = UIHostingController(rootView: contentView)
            self.window = window
            window.makeKeyAndVisible()
        }
        
        // ...
    }

이렇게 하면 다음과 같은 결과를 얻는다

 

  1. 씬을 초기화 할 때 DataModel의 state 프로퍼티를 0으로 초기화해 생성한다.
  2. 컨텐트 뷰를 미리 생성한 DataModel과 함께 생성한다.
  3. DispatchQueue를 이용해 글로벌 스레드에서 비동기 작업으로 다음과 같은 작업을 무한 반복한다.
    1. 메인 스레드에서 contentView의 data에 데이터가 바뀐다는 신호를 보낸다.
    2. 의사코드: dataModel에 대한 업데이트 작업을 진행한다.
    3. 0.5초 대기한다.
  4. 기본 씬 생성 코드...

이렇게 하면 외부 코드(여기서는 PseudoUpdater의 refresh 메서드)에 따라서 dataModel 내의 데이터가 변경될 것이고 이에 우리는 변경될 것이라는 것을 SwiftUI에 알려줬으니 수치가 변경 되면 알아서 반영해줄 것이다.

 

 

 

p.s. 별 거 아닌데 내 조잡한 영어 실력으로 구글링 하면 죄다 BindableObject 같은 말도 안 되는 Deprecated 된 예시만 들어서 몇 시간 꼴았다. 갈 곳 잃은 분노를 식히고 하루 지나서야 포스트 작성.

이미지의 행렬의 수와 비어있는 스프라이트의 갯수를 알면 된다.

 

아래는 이해를 편하게 하기 위한 코드화

MaxSpriteCount = Row * Column
RealCount = MaxSpriteCount - Offset

MaxAnimationPhase = RealCount / MaxSpriteCount
AnimationTimePhase = Frac(Time)

// 소수점 오차로 인한 Blinking을 피하기 위해 MaxAnimationPhase에 Subtract
CurrentAnimationPhase = Lerp(0, MaxAnimationPhase - .000001, AnimationTimePhase)

 

seq[uint8] => uint32

공부/Nim2019. 5. 25. 08:11
proc to_uint32(self: seq[uint8]): uint32 =
  if self.len() != 4:
    return 0
  result = (cast[uint32](self[3]) shl 0) or
          (cast[uint32](self[2]) shl 8) or
          (cast[uint32](self[1]) shl 16) or
          (cast[uint32](self[0]) shl 24)

proc to_uint8_seq(self: uint32): seq[uint8] =
  result = @[]
  result.add(cast[uint8](self shr 0))
  result.add(cast[uint8](self shr 8))
  result.add(cast[uint8](self shr 16))
  result.add(cast[uint8](self shr 24))

when isMainModule:
  let sample = @[0x00'u8, 0x00, 0x00, 0x10]
  echo sample.to_uint32()                    # 16
  echo sample.to_uint32().to_uint8_seq()    # @[0, 0, 0, 16]

다음 참조: http://wapel.de/?p=603

위에 나온 deps 모두 깔고 다음 명령어 실행

sudo apt-get install libc6-armhf-cross libc6-dev-armhf-cross
sudo ln -s /lib/arm-linux-gnueabihf/libpthead-2.14.so /usr/lib/arm-linux-gnueabihf/libpthread.so.0
sudo rm -rf /usr/lib/chromium-browser/libs

여기 에서 raspberry-gcc6.3.0-r4.exe를 설치하고 TOOLS/UpdateSysroot.bat를 실행한다.
다음 경로를 붙여넣는다.

/lib
/usr/include
/usr/lib
/usr/local/include
/usr/local/lib
/opt

이제 하단 체크박스를 클릭하고 sync 한다.

./configure -release -opengl es2 -device linux-rasp-pi3-g++ -platform win32-g++ -device-option CROSS_COMPILE="/c/SysGCC/raspberry/bin/arm-linux-gnueabihf-" -sysroot /c/SysGCC/raspberry/arm-linux-gnueabihf/sysroot -opensource -confirm-license -make libs -prefix /usr/local/qt5pi -extprefix /root/qt5pi -hostprefix /root/qt5 -no-use-gold-linker -v -no-gbm -nomake examples -nomake tests -qt-freetype -qt-harfbuzz -skip qtscript -no-xcb

2019.05.04 deps 수정

http://www.michalorzelek.com/blog/tutorial-creating-outline-effect-around-objects/


  1. 위 링크 가서 마테리얼 받아준 뒤 콘텐츠 폴더에 넣습니다.
  2. 위 링크에서 나온대로 PostProcess 설정을 합니다.
  3. Build.cs로 가서 PublicDependencyModuleNames에 ProceduralMeshComponent를 추가해 줍니다.
  4. 언리얼 에디터로 가서 C++ 클래스를 추가합니다. 언리얼은 투명한 Mesh에는 아웃라인을 그려주지 않아서 투명하지 않은 더미 렌더러를 만들 것입니다.
    1. 모든 클래스 표시
    2. SpineSkeletonRendererComponent를 부모 클래스로 선택합니다.
    3. 이름을 정하고 생성합니다. 이 예제에서는 OutlineRenderComponent로 가정하겠습니다.
  5. 생성된 헤더 파일에 생성자를 만듭니다. 인수는 const FObjectInitializer& ObjectInitializer입니다.
  6. 소스 파일에도 구현부를 생성합니다. 부모 클래스 초기화도 잊지 마세요.
  7. 생성자 구현부에 다음과 같은 코드를 씁니다. http://cpp.sh/9y2zl
    1. /Game/Materials/Spine/SpineUnlitNormalMaterial로 부터 머티리얼 인스턴스를 가져옵니다.
    2. NormalBlendMaterial 변수에 가져온 머리티얼 인스턴스 오브젝트를 넣었습니다.
    3. bRenderInMainPass를 비활성화 해 (간단히 설명해) 모습을 숨기고 아웃라인에 필요한 Depth가 검출 가능하도록 렌더링 합니다.
  8. 언리얼 에디터로 돌아와 콘텐츠 브라우저 하단에 뷰 옵션에서 플러그인 콘텐츠를 볼 수 있도록 토글합니다.
  9. SpinePlugin 콘텐츠에서 SpineUnlitNormalMaterial를 복사해 /Game/Materials/Spine/ 폴더 안에 복사합니다. 폴더가 없다면 만듭니다.
  10. 복사한 머티리얼을 열어 Blend 모드를 Masked로 변경하고 오파시티에 있는 핀의 경로에 존재하는 노드를 오파시티 마스크에 연결합니다.
  11. 저장한 뒤 SpineSkeletonRendererComponent와 SpineSkeletonAnimationComponent가 있는 액터에 OutlineRenderComponent도 붙여줍니다.
    1. 가시성을 위해 SpineSkeletonRendererComponent에 Attach 해주는 게 좋을 것 같다고 개인적으로 생각합니다.
    2. Spine Runtime은 Owner의 모든 컴포넌트를 찾아서 동작하므로 Attach 관계는 상관 없습니다.
  12. 이제 OutlineRenderComponent의 SetRenderCustomDepth(true)를 호출하면 설정한 대로 외곽선이 표시됩니다.


극혐 에러입니다. 구글링 해도 해결 방법도 없고...


MO를 쓰다가 MO2로 넘어왔는데 (LE 버전 스카이림)

갑자기 FNIS가 작동을 안 하더군요.


겪은 문제점은 다음과 같습니다:

  • 스카이림 버전을 1.8.151로 인식함
  • 스카이림 설치가 잘못되었다고 함
  • 에러코드 2012 (파일을 생성할 수 없음)



해결 방법은 다음과 같습니다.


  1. overwrite 등에 있는 (data)\tools에 있는 FNIS 관련 파일을 모두 지운다
  2. MO에서 FNIS 안에 있는 tools 폴더를 "스카이림 폴더\Data" 안에 넣는다
  3. 옮긴 파일을 MO로 연다.


해결.

우리는 앞서 재장전 시스템을 만들었다. 그럼 이제 연사가 가능한 총을 만들어 보자.


우선 마우스 왼쪽 버튼이 release 됐을 때 행동을 구현해야 한다. 완전 간단하다. Character.h를 열어 총을 쏘고있는지 체크할 bool 타입의 변수 하나와 StopFire() 함수를 정의하고 Character.cpp에서 구현과 입력 설정을 마치자.

bool isFiring;
...중략...
void StopFire();
...후략...
void AFPSCppCharacter::StopFire() {
	isFiring = false;
}
InputComponent->BindAction("Fire", IE_Released, this, &AFPSCppCharacter::StopFire);


이제 마우스 왼쪽 버튼을 누르고 있다면 탄환 발사 코드가 계속 반복되어야 한다. 하지만 그저 반복 코드만 넣는다면 결국 무한 루프에 빠져 프로그램은 멈출 것이다.  그렇다면 우리가 필요한 것은 딜레이다. 이제부터 그걸 만들어 보자.


일단 Character.h에 타이머를 정의해주자.

FTimerHandle _timer;


그 다음 Character.cpp를 열어서 OnFire() 함수 구현부를 if문으로 감싸서 isFiring을 체크하도록 만들고 하단 부에 타이머를 넣는다.

if (isFiring) {
	중략...

	GetWorld()->GetTimerManager().SetTimer(_timer, this, &AFPSCppCharacter::OnFire, .075f, false);
}


이렇게 쓴다면 발사 버튼을 누를 때 모든 코드를 최소한 한 번은 실행하고 그 다음 isFiring이 true라면 0.075초 대기한 다음, AFPSCppCharacter의 OnFire 함수를 부를 것이다. 그렇다. 즉 이 코드는 OnFire를 재귀함수로 만들어준다.


결국 버튼이 release 될 때까지 발사 코드를 실행하고 0.075초를 기다린 후 다시 발사하는 코드인 것이다.


하지만 무언가가 빠졌다. 우리는 isFiring을 초기화한 적이 없다. 그럼 이제 알아보기 쉽게 왼쪽 버튼을 눌렀을 때 isFiring을 초기화 해보자.


다음 코드를 찾는다.

InputComponent->BindAction("Fire", IE_Pressed, this, &AFPSCppCharacter::OnFire);


다음과 같이 바꾼다.

InputComponent->BindAction("Fire", IE_Pressed, this, &AFPSCppCharacter::StartFire);


이제 StartFire()를 Character.h에서 정의하고, Character.cpp에서 다음과 같이 구현한다.


void AFPSCppCharacter::StartFire() {
	isFiring = true;
	OnFire();
}


그럼 이제 마우스 왼쪽 버튼을 누르면 isFiring을 true 값으로 바꾸고 OnFire() 함수로 진입할 것이다. 그렇다면 이제 버튼을 누르면 총을 쏘고, 버튼을 놓으면 발사가 멈출 것이다.